Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1519 lines
44 KiB
C++
1519 lines
44 KiB
C++
#include "ElementRendererHeaders.hpp"
|
|
#include <MLR\MLRInfiniteLightWithFalloff.hpp>
|
|
|
|
//#define SPEW_CULL_INFO "your_name_here"
|
|
|
|
//############################################################################
|
|
//############################ TreeElement ################################
|
|
//############################################################################
|
|
|
|
HGOSHEAP ElementRenderer::TreeElement::s_Heap = NULL;
|
|
|
|
ElementRenderer::TreeElement::ClassData* ElementRenderer::TreeElement::DefaultData = NULL;
|
|
|
|
ElementRenderer::Element::SyncMethod
|
|
ElementRenderer::TreeElement::SyncMethods[SyncStateCount]=
|
|
{
|
|
//
|
|
// Root mode
|
|
//
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// Sphere Cull
|
|
//
|
|
SYNC_METHOD(TreeElement, CleanSphere),
|
|
SYNC_METHOD(TreeElement, DirtySphere),
|
|
SYNC_METHOD(TreeElement, CleanSphere),
|
|
SYNC_METHOD(TreeElement, DirtySphere),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// OBB Cull
|
|
//
|
|
SYNC_METHOD(TreeElement, CleanOBB),
|
|
SYNC_METHOD(TreeElement, DirtyOBB),
|
|
SYNC_METHOD(TreeElement, CleanOBB),
|
|
SYNC_METHOD(TreeElement, DirtyOBB),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// Never Cull
|
|
//
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(TreeElement, Clean),
|
|
SYNC_METHOD(TreeElement, Dirty),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// Always Cull
|
|
//
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, None)
|
|
};
|
|
|
|
ElementRenderer::Element::DrawMethod ElementRenderer::TreeElement::DrawMethods[DrawStateCount]=
|
|
{
|
|
DRAW_METHOD(TreeElement, Inherit),
|
|
DRAW_METHOD(TreeElement, Override)
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::InitializeClass()
|
|
{
|
|
Verify(!DefaultData);
|
|
|
|
Verify(!s_Heap);
|
|
s_Heap = gos_CreateMemoryHeap("TreeLOD", 0, g_LibraryHeap);
|
|
Check_Pointer(s_Heap);
|
|
|
|
DefaultData =
|
|
new ClassData(
|
|
TreeElementClassID,
|
|
"ElementRenderer::TreeElement",
|
|
BaseClass::DefaultData,
|
|
reinterpret_cast<Factory>(&Make)
|
|
);
|
|
Check_Object(DefaultData);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::TerminateClass()
|
|
{
|
|
Check_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
|
|
Check_Pointer(s_Heap);
|
|
gos_DestroyMemoryHeap(s_Heap);
|
|
s_Heap = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::TreeElement::TreeElement(
|
|
Stuff::MemoryStream *stream,
|
|
int version,
|
|
ShapeHolder shapes
|
|
):
|
|
Element(DefaultData, stream, version)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(stream);
|
|
|
|
if (version<14)
|
|
STOP(("Application must be recompiled to use this data!"));
|
|
unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
|
|
Verify(index < SyncStateCount);
|
|
m_sync = SyncMethods[index];
|
|
index = (m_state>>CullStateBit) & CullStateMask;
|
|
Verify(index < CullStateCount);
|
|
m_cameraCull = CullMethods[index].m_cameraCull;
|
|
m_rayCull = CullMethods[index].m_rayCull;
|
|
m_sphereTest = CullMethods[index].m_sphereTest;
|
|
index = (m_state>>DrawStateBit) & DrawStateMask;
|
|
Verify(index < DrawStateCount);
|
|
m_draw = DrawMethods[index];
|
|
|
|
//
|
|
//----------------------
|
|
// Read in the LOD count
|
|
//----------------------
|
|
//
|
|
m_worldToLocal = Stuff::LinearMatrix4D::Identity;
|
|
WORD count;
|
|
*stream >> m_startAligningAt >> m_alignType >> count;
|
|
m_ranges.SetLength(count);
|
|
m_MLRShapes.SetLength(count);
|
|
int mlr_version = MidLevelRenderer::ReadMLRVersion(stream);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Newer versions only load in the parts of the Multi-LOD that are visible
|
|
//------------------------------------------------------------------------
|
|
//
|
|
WORD used=0;
|
|
for (WORD i=0; i<count; ++i)
|
|
{
|
|
*stream >> m_ranges[used].m_nearSquared >> m_ranges[used].m_fadeInSquared;
|
|
*stream >> m_ranges[used].m_fadeOutSquared >> m_ranges[used].m_farSquared;
|
|
|
|
//
|
|
//-----------------------------------------------------
|
|
// If this can't be seen because of the offset, dump it
|
|
//-----------------------------------------------------
|
|
//
|
|
int length;
|
|
*stream >> length;
|
|
if (i<count-1 && LODElement::s_Offset > m_ranges[used].m_farSquared)
|
|
{
|
|
stream->AdvancePointer(length);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------------------
|
|
// If this is the first visible LOD after we have dropped one, reset the near clipping
|
|
// plane to zero
|
|
//------------------------------------------------------------------------------------
|
|
//
|
|
else if (!used && i>0)
|
|
{
|
|
m_ranges[used].m_nearSquared = 0.0f;
|
|
m_ranges[used].m_fadeInSquared = 0.0f;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If we were given a shape holder, have it make the MLR shape for us
|
|
//-------------------------------------------------------------------
|
|
//
|
|
if (shapes)
|
|
m_MLRShapes[used] = (*shapes)(stream, mlr_version, length);
|
|
|
|
//
|
|
//---------------------------------------------------------------
|
|
// Otherwise, read in the class ID and call the right constructor
|
|
//---------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
RegisteredClass::ClassID class_id;
|
|
*stream >> class_id;
|
|
switch(class_id)
|
|
{
|
|
case MidLevelRenderer::MLRShapeClassID:
|
|
m_MLRShapes[used] = MidLevelRenderer::MLRShape::Make(stream, mlr_version);
|
|
break;
|
|
case MidLevelRenderer::MLRCulturShapeClassID:
|
|
m_MLRShapes[used] = MidLevelRenderer::MLRCulturShape::Make(stream, mlr_version);
|
|
break;
|
|
}
|
|
}
|
|
Check_Object(m_MLRShapes[used]);
|
|
++used;
|
|
}
|
|
Verify(used>0);
|
|
if (used < count)
|
|
{
|
|
m_ranges.SetLength(used);
|
|
m_MLRShapes.SetLength(used);
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Bump the OBB a tad to help with clipping errors
|
|
//------------------------------------------------
|
|
//
|
|
if (IsBoundedBySphere())
|
|
m_localOBB.sphereRadius *= 1.01f;
|
|
else
|
|
{
|
|
m_localOBB.axisExtents *= 1.01f;
|
|
m_localOBB.sphereRadius = m_localOBB.axisExtents.GetLength();
|
|
}
|
|
if (ShapeElement::s_CheckBounds && !CheckBounds())
|
|
ShapeElement::s_BoundsWereBad = true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::TreeElement::TreeElement():
|
|
Element(DefaultData)
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
|
|
Verify(index < SyncStateCount);
|
|
m_sync = SyncMethods[index];
|
|
index = (m_state>>CullStateBit) & CullStateMask;
|
|
Verify(index < CullStateCount);
|
|
m_cameraCull = CullMethods[index].m_cameraCull;
|
|
m_rayCull = CullMethods[index].m_rayCull;
|
|
m_sphereTest = CullMethods[index].m_sphereTest;
|
|
index = (m_state>>DrawStateBit) & DrawStateMask;
|
|
Verify(index < DrawStateCount);
|
|
m_draw = DrawMethods[index];
|
|
|
|
m_worldToLocal = Stuff::LinearMatrix4D::Identity;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::TreeElement::~TreeElement()
|
|
{
|
|
Check_Object(this);
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Check_Object(m_MLRShapes[i]);
|
|
m_MLRShapes[i]->DetachReference();
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::TreeElement* ElementRenderer::TreeElement::Make(
|
|
Stuff::MemoryStream *stream,
|
|
int version,
|
|
ShapeHolder shapes
|
|
)
|
|
{
|
|
Check_Object(stream);
|
|
|
|
gos_PushCurrentHeap(s_Heap);
|
|
TreeElement *element = new TreeElement(stream, version, shapes);
|
|
gos_PopCurrentHeap();
|
|
|
|
return element;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::Save(Stuff::MemoryStream *stream)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
BaseClass::Save(stream);
|
|
|
|
//
|
|
//-----------------------------
|
|
// Save the shape to the stream
|
|
//-----------------------------
|
|
//
|
|
Verify(m_ranges.GetLength() < 65536);
|
|
*stream << m_startAligningAt << m_alignType << static_cast<WORD>(m_ranges.GetLength());
|
|
MidLevelRenderer::WriteMLRVersion(stream);
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Check_Object(m_MLRShapes[i]);
|
|
|
|
//
|
|
//------------------------
|
|
// Save out the LOD ranges
|
|
//------------------------
|
|
//
|
|
*stream << m_ranges[i].m_nearSquared << m_ranges[i].m_fadeInSquared;
|
|
*stream << m_ranges[i].m_fadeOutSquared << m_ranges[i].m_farSquared;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------------
|
|
// Figure out how much space this particular MLRShape uses, and put that into the
|
|
// length pointer
|
|
//-------------------------------------------------------------------------------
|
|
//
|
|
*stream << (int)0;
|
|
unsigned index = stream->GetIndex();
|
|
m_MLRShapes[i]->Save(stream);
|
|
unsigned length = stream->GetIndex() - index;
|
|
stream->RewindPointer(length + sizeof(int));
|
|
*stream << length;
|
|
stream->AdvancePointer(length);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::TestInstance()
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool ElementRenderer::TreeElement::CheckBounds()
|
|
{
|
|
Check_Object(this);
|
|
|
|
int meshes = m_MLRShapes.GetLength();
|
|
for(int m=0; m<meshes; m++)
|
|
{
|
|
bool check;
|
|
if (IsBoundedBySphere())
|
|
check = ShapeElement::CheckSphereBounds(m_MLRShapes[m], m_localOBB);
|
|
else
|
|
check = ShapeElement::CheckOBBBounds(m_MLRShapes[m], m_localOBB);
|
|
if (!check)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::AttachChild(Element *child)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(child);
|
|
STOP(("Shape elements can't have children!"));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::DetachChild(Element *child)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(child);
|
|
STOP(("Shape elements can't have children!"));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::AttachLOD(
|
|
WORD index,
|
|
MidLevelRenderer::MLRShape *shape,
|
|
const Entry &entry
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(shape);
|
|
|
|
if (m_MLRShapes[index])
|
|
{
|
|
Check_Object(m_MLRShapes[index]);
|
|
m_MLRShapes[index]->DetachReference();
|
|
}
|
|
m_MLRShapes[index] = shape;
|
|
m_MLRShapes[index]->AttachReference();
|
|
m_ranges[index] = entry;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// This is necessary for attaching LOD's into the list that might miss certain
|
|
// meshes in the middle ie.. 4/5 of the LOD's have a toe, but lod 2 doesn't
|
|
// Needed for the exporter. The grouping there doesn't allow counters.
|
|
int ElementRenderer::TreeElement::AttachLODFirstAvailableSlot(
|
|
MidLevelRenderer::MLRShape *shape,
|
|
const Entry &entry
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(shape);
|
|
|
|
for (unsigned short index = 0; index < m_MLRShapes.GetLength(); index ++)
|
|
{
|
|
|
|
if (m_MLRShapes[index] == NULL)
|
|
{
|
|
m_MLRShapes[index] = shape;
|
|
m_MLRShapes[index]->AttachReference();
|
|
m_ranges[index] = entry;
|
|
return index;
|
|
}
|
|
|
|
}
|
|
STOP(("TreeElement: Can't attach another LOD, already full!"));
|
|
return -1;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::GetLOD(
|
|
WORD index,
|
|
MidLevelRenderer::MLRShape **shape,
|
|
const Entry **ranges
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
Check_Object(m_MLRShapes[index]);
|
|
*shape = m_MLRShapes[index];
|
|
(*shape)->AttachReference();
|
|
*ranges = &m_ranges[index];
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::SetLOD(
|
|
WORD index,
|
|
const Entry &ranges
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
m_ranges[index]=ranges;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::SetSize(WORD max_size)
|
|
{
|
|
Check_Object(this);
|
|
DWORD i = m_MLRShapes.GetLength();
|
|
m_MLRShapes.SetLength(max_size);
|
|
m_ranges.SetLength(max_size);
|
|
for (;i<max_size; ++i)
|
|
{
|
|
m_MLRShapes[i] = NULL;
|
|
m_ranges[i].m_nearSquared = 0.0f;
|
|
m_ranges[i].m_fadeInSquared = 0.0f;
|
|
m_ranges[i].m_fadeOutSquared = 0.0f;
|
|
m_ranges[i].m_farSquared = 0.0f;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
WORD ElementRenderer::TreeElement::GetSize()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_MLRShapes.GetLength() < 65536);
|
|
return static_cast<WORD>(m_MLRShapes.GetLength());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::SetSyncState()
|
|
{
|
|
Check_Object(this);
|
|
|
|
Verify(
|
|
GetCullMode() != VolumeCullMode
|
|
|| AreBoundsWrong()
|
|
|| IsSyncDeferred()
|
|
|| (
|
|
m_localOBB.sphereRadius>0.0f
|
|
&& (IsMatrixDirty() || m_worldOBB.sphereRadius>0.0f
|
|
)
|
|
)
|
|
);
|
|
Verify(
|
|
GetCullMode() == RootMode || m_parent != NULL || AreBoundsWrong()
|
|
);
|
|
unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
|
|
Verify(index < SyncStateCount);
|
|
m_sync = SyncMethods[index];
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::DirtySyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(IsMatrixDirty() && GetCullMode()==NeverCullMode);
|
|
SYNC_LOGIC("Dirty::Tree");
|
|
|
|
//
|
|
//--------------------
|
|
// Update our matrices
|
|
//--------------------
|
|
//
|
|
m_localToParent = m_newLocalToParent;
|
|
Check_Object(m_parent);
|
|
if (!IsLocalToParentIdentity())
|
|
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
|
|
else
|
|
m_localToWorld = m_parent->GetLocalToWorld();
|
|
m_worldToLocal.Invert(m_localToWorld);
|
|
MatrixIsClean();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::CleanSphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
Verify(
|
|
!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Clean::Tree");
|
|
|
|
//
|
|
//-------------------------------
|
|
// Update our m_localToWorld matrix
|
|
//-------------------------------
|
|
//
|
|
Check_Object(m_parent);
|
|
if (!IsLocalToParentIdentity())
|
|
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
|
|
else
|
|
m_localToWorld = m_parent->GetLocalToWorld();
|
|
m_worldToLocal.Invert(m_localToWorld);
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::DirtySphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
Verify(
|
|
IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Dirty::Tree");
|
|
|
|
//
|
|
//--------------------
|
|
// Update our matrices
|
|
//--------------------
|
|
//
|
|
m_localToParent = m_newLocalToParent;
|
|
Check_Object(m_parent);
|
|
if (!IsLocalToParentIdentity())
|
|
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
|
|
else
|
|
m_localToWorld = m_parent->GetLocalToWorld();
|
|
m_worldToLocal.Invert(m_localToWorld);
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
MatrixIsClean();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::CleanOBBSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(
|
|
!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB()
|
|
);
|
|
Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f));
|
|
SYNC_LOGIC("OBB::Clean::Tree");
|
|
|
|
//
|
|
//-------------------------------
|
|
// Update our m_localToWorld matrix
|
|
//-------------------------------
|
|
//
|
|
Check_Object(m_parent);
|
|
if (!IsLocalToParentIdentity())
|
|
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
|
|
else
|
|
m_localToWorld = m_parent->GetLocalToWorld();
|
|
m_worldToLocal.Invert(m_localToWorld);
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::DirtyOBBSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(
|
|
IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB()
|
|
);
|
|
Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f));
|
|
SYNC_LOGIC("OBB::Dirty::Tree");
|
|
|
|
//
|
|
//--------------------
|
|
// Update our matrices
|
|
//--------------------
|
|
//
|
|
m_localToParent = m_newLocalToParent;
|
|
Check_Object(m_parent);
|
|
if (!IsLocalToParentIdentity())
|
|
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
|
|
else
|
|
m_localToWorld = m_parent->GetLocalToWorld();
|
|
m_worldToLocal.Invert(m_localToWorld);
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
|
|
MatrixIsClean();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool ElementRenderer::TreeElement::CastCulledRay(CollisionQuery *query)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(query);
|
|
Verify(!query->m_data);
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Figure out the local m_line equation
|
|
//-----------------------------------
|
|
//
|
|
const Stuff::LinearMatrix4D &world_to_local = GetWorldToLocal();
|
|
Check_Object(&world_to_local);
|
|
Stuff::Line3D local_line;
|
|
Check_Object(query->m_line);
|
|
local_line.m_origin.Multiply(query->m_line->m_origin, world_to_local);
|
|
local_line.m_direction.Multiply(query->m_line->m_direction, world_to_local);
|
|
local_line.m_length = query->m_line->m_length;
|
|
|
|
//
|
|
//-----------------------------
|
|
// Test it against the MidLevelRenderer::MLRShape
|
|
//-----------------------------
|
|
//
|
|
Stuff::Normal3D local_normal;
|
|
if (m_MLRShapes.GetLength()>0)
|
|
{
|
|
Check_Object(m_MLRShapes[0]);
|
|
if (m_MLRShapes[0]->CastRay(&local_line, &local_normal))
|
|
{
|
|
query->m_line->m_length = local_line.m_length;
|
|
const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld();
|
|
Check_Object(&local_to_world);
|
|
Check_Pointer(query->m_normal);
|
|
query->m_normal->Multiply(local_normal, local_to_world);
|
|
Verify(*query->m_normal * query->m_line->m_direction <= -Stuff::SMALL);
|
|
query->m_data = m_data;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::Element* ElementRenderer::TreeElement::FindSmallestElementContainingCulled(SphereTest *test)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(test);
|
|
return this;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::SetDrawState()
|
|
{
|
|
Check_Object(this);
|
|
unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
|
|
Verify(index < DrawStateCount);
|
|
m_draw = DrawMethods[index];
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::InheritDrawMethod(
|
|
CameraElement *camera,
|
|
const StateChange *inherited_state,
|
|
int culling_state
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(inherited_state);
|
|
Check_Object(camera);
|
|
Verify(culling_state != -1);
|
|
#if defined(_ARMOR)
|
|
if (!culling_state)
|
|
Verify(!Cull(camera));
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Draw our bounds if we are supposed to
|
|
//--------------------------------------
|
|
//
|
|
#if defined(LAB_ONLY)
|
|
Callback *callback = GetCallbackSet();
|
|
unsigned index = GetCallbackIndex();
|
|
if (callback && callback[index])
|
|
{
|
|
Check_Pointer(callback[index]);
|
|
(*callback[index])(camera, inherited_state, culling_state, this);
|
|
}
|
|
else if (ShapeElement::s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state)
|
|
{
|
|
Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f);
|
|
if (IsBoundedBySphere())
|
|
DrawSphere(m_worldOBB, color, camera, inherited_state, culling_state);
|
|
else
|
|
DrawOBB(m_worldOBB, color, camera, inherited_state, culling_state);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// We need to find out which lod the camera should traverse into, so
|
|
// transform the camera into LOD space and get the distance from the LOD
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Stuff::Point3D camera_origin(camera->GetLocalToWorld());
|
|
Stuff::Point3D lod_origin(GetLocalToWorld());
|
|
camera_origin -= lod_origin;
|
|
Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset;
|
|
Min_Clamp(distance, 0.0f);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Evaluate each LOD to see if it needs to be drawn
|
|
//-------------------------------------------------
|
|
//
|
|
for (int i=m_ranges.GetLength()-1; i>=0; --i)
|
|
{
|
|
Entry *entry = &m_ranges[i];
|
|
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
|
|
{
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Set the fade color if we are in the band
|
|
//-----------------------------------------
|
|
//
|
|
StateChange mixed(*inherited_state);
|
|
unsigned j;
|
|
for (j=0; j<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive && inherited_state->m_lights[j]; ++j)
|
|
{
|
|
Check_Object(inherited_state->m_lights[j]);
|
|
mixed.m_lights[j] = inherited_state->m_lights[j];
|
|
}
|
|
if(j<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive)
|
|
{
|
|
mixed.m_lights[j] = NULL;
|
|
}
|
|
|
|
Stuff::Scalar fade = FadeColor.alpha;
|
|
if (distance < entry->m_fadeInSquared)
|
|
{
|
|
Stuff::Scalar temp =
|
|
(distance - entry->m_nearSquared) / (entry->m_fadeInSquared - entry->m_nearSquared);
|
|
Verify(temp >= 0.0f && temp <= 1.0f);
|
|
FadeColor.alpha *= temp;
|
|
mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
|
|
mixed.DisableChildAlphaControl();
|
|
mixed.SetRenderPriority(StateChange::AlphaPriority);
|
|
mixed.DisableChildRenderPriorityControl();
|
|
}
|
|
else if (distance > entry->m_fadeOutSquared)
|
|
{
|
|
Stuff::Scalar temp =
|
|
(entry->m_farSquared - distance) / (entry->m_farSquared - entry->m_fadeOutSquared);
|
|
Verify(temp >= 0.0f && temp <= 1.0f);
|
|
FadeColor.alpha *= temp;
|
|
mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
|
|
mixed.DisableChildAlphaControl();
|
|
mixed.SetRenderPriority(StateChange::AlphaPriority);
|
|
mixed.DisableChildRenderPriorityControl();
|
|
}
|
|
|
|
//
|
|
//----------------------------
|
|
// Set up the draw information
|
|
//----------------------------
|
|
//
|
|
MidLevelRenderer::DrawShapeInformation info;
|
|
Check_Object(m_MLRShapes[i]);
|
|
info.shape = m_MLRShapes[i];
|
|
info.clippingFlags.SetClippingState(culling_state);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// If we are far enough away, do the aligning thing
|
|
//-------------------------------------------------
|
|
//
|
|
Stuff::LinearMatrix4D new_local_to_world, new_world_to_local;
|
|
if (i>=m_startAligningAt)
|
|
{
|
|
switch (m_alignType)
|
|
{
|
|
case AlignXFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::X_Axis, -1);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
case AlignYFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::Y_Axis, -1);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
case AlignXFlag|AlignYFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::Y_Axis, Stuff::X_Axis);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
STOP(("Bad align type"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Otherwise, use the data burned into the element
|
|
//------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &m_localToWorld;
|
|
Check_Object(&m_worldToLocal);
|
|
info.worldToShape = &m_worldToLocal;
|
|
}
|
|
|
|
info.state = mixed.GetMLRState();
|
|
#if defined(LAB_ONLY)
|
|
if (LODElement::s_ShadeLODs && i<ELEMENTS(LODElement::s_Shades))
|
|
info.paintMeColor = &LODElement::s_Shades[i];
|
|
else
|
|
#endif
|
|
info.paintMeColor = &FadeColor;
|
|
LightList culled_lights;
|
|
info.nrOfActiveLights = CullLights(culled_lights, inherited_state->m_lights);
|
|
info.activeLights = (info.nrOfActiveLights>0) ? &culled_lights[0] : NULL;
|
|
|
|
//
|
|
//---------------
|
|
// Draw the shape
|
|
//---------------
|
|
//
|
|
MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
|
|
Check_Object(clipper);
|
|
|
|
Stop_Timer(Graph_Traversal);
|
|
ELEMENT_RENDER("Culling::Tree::Inherited");
|
|
Start_Timer(Draw_Shapes);
|
|
clipper->DrawShape(&info);
|
|
Stop_Timer(Draw_Shapes);
|
|
Start_Timer(Graph_Traversal);
|
|
|
|
FadeColor.alpha = fade;
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void ElementRenderer::TreeElement::OverrideDrawMethod(
|
|
CameraElement *camera,
|
|
const StateChange *inherited_state,
|
|
int culling_state
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(inherited_state);
|
|
Check_Object(camera);
|
|
Verify(culling_state != -1);
|
|
#if defined(_ARMOR)
|
|
if (!culling_state)
|
|
Verify(!Cull(camera));
|
|
#endif
|
|
|
|
//
|
|
//-------------------
|
|
// Mix the properties
|
|
//-------------------
|
|
//
|
|
Check_Object(m_stateChange);
|
|
StateChange mixed;
|
|
mixed.Mix(*inherited_state, *m_stateChange);
|
|
MixLights(
|
|
mixed.m_lights,
|
|
inherited_state->m_lights,
|
|
m_stateChange->m_lights
|
|
);
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Draw our bounds if we are supposed to
|
|
//--------------------------------------
|
|
//
|
|
#if defined(LAB_ONLY)
|
|
Callback *callback = GetCallbackSet();
|
|
unsigned index = GetCallbackIndex();
|
|
if (callback && callback[index])
|
|
{
|
|
Check_Pointer(callback[index]);
|
|
(*callback[index])(camera, &mixed, culling_state, this);
|
|
}
|
|
else if (ShapeElement::s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state)
|
|
{
|
|
Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f);
|
|
if (IsBoundedBySphere())
|
|
DrawSphere(m_worldOBB, color, camera, &mixed, culling_state);
|
|
else
|
|
DrawOBB(m_worldOBB, color, camera, &mixed, culling_state);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// We need to find out which lod the camera should traverse into, so
|
|
// transform the camera into LOD space and get the distance from the LOD
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Stuff::Point3D camera_origin(camera->GetLocalToWorld());
|
|
Stuff::Point3D lod_origin(GetLocalToWorld());
|
|
camera_origin -= lod_origin;
|
|
Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset;
|
|
Min_Clamp(distance, 0.0f);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Evaluate each LOD to see if it needs to be drawn
|
|
//-------------------------------------------------
|
|
//
|
|
for (int i=m_ranges.GetLength()-1; i>=0; --i)
|
|
{
|
|
Entry *entry = &m_ranges[i];
|
|
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
|
|
{
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Set the fade color if we are in the band
|
|
//-----------------------------------------
|
|
//
|
|
Stuff::Scalar fade = FadeColor.alpha;
|
|
if (distance < entry->m_fadeInSquared)
|
|
{
|
|
Stuff::Scalar temp =
|
|
(distance - entry->m_nearSquared) / (entry->m_fadeInSquared - entry->m_nearSquared);
|
|
Verify(temp >= 0.0f && temp <= 1.0f);
|
|
FadeColor.alpha *= temp;
|
|
mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
|
|
mixed.DisableChildAlphaControl();
|
|
mixed.SetRenderPriority(StateChange::AlphaPriority);
|
|
mixed.DisableChildRenderPriorityControl();
|
|
}
|
|
else if (distance > entry->m_fadeOutSquared)
|
|
{
|
|
Stuff::Scalar temp =
|
|
(entry->m_farSquared - distance) / (entry->m_farSquared - entry->m_fadeOutSquared);
|
|
Verify(temp >= 0.0f && temp <= 1.0f);
|
|
FadeColor.alpha *= temp;
|
|
mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
|
|
mixed.DisableChildAlphaControl();
|
|
mixed.SetRenderPriority(StateChange::AlphaPriority);
|
|
mixed.DisableChildRenderPriorityControl();
|
|
}
|
|
|
|
//
|
|
//----------------------------
|
|
// Set up the draw information
|
|
//----------------------------
|
|
//
|
|
MidLevelRenderer::DrawShapeInformation info;
|
|
Check_Object(m_MLRShapes[i]);
|
|
info.shape = m_MLRShapes[i];
|
|
info.clippingFlags.SetClippingState(culling_state);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// If we are far enough away, do the aligning thing
|
|
//-------------------------------------------------
|
|
//
|
|
Stuff::LinearMatrix4D new_local_to_world, new_world_to_local;
|
|
if (i>=m_startAligningAt)
|
|
{
|
|
switch (m_alignType)
|
|
{
|
|
case AlignXFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::X_Axis, -1);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
case AlignYFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::Y_Axis, -1);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
case AlignXFlag|AlignYFlag:
|
|
{
|
|
Check_Object(m_parent);
|
|
Stuff::Point3D world_camera(camera->GetLocalToWorld());
|
|
Stuff::Point3D parent_camera;
|
|
parent_camera.MultiplyByInverse(
|
|
world_camera,
|
|
m_parent->GetLocalToWorld()
|
|
);
|
|
Stuff::Vector3D camera_direction;
|
|
Stuff::Point3D local_origin(m_newLocalToParent);
|
|
camera_direction.Subtract(parent_camera, local_origin);
|
|
Stuff::LinearMatrix4D new_local_to_parent(m_newLocalToParent);
|
|
new_local_to_parent.AlignLocalAxisToWorldVector(camera_direction, Stuff::Z_Axis, Stuff::Y_Axis, Stuff::X_Axis);
|
|
new_local_to_world.Multiply(new_local_to_parent, m_parent->GetLocalToWorld());
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &new_local_to_world;
|
|
new_world_to_local.Invert(new_local_to_world);
|
|
info.worldToShape = &new_world_to_local;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
STOP(("Bad align type"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Otherwise, use the data burned into the element
|
|
//------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Check_Object(&m_localToWorld);
|
|
info.shapeToWorld = &m_localToWorld;
|
|
Check_Object(&m_worldToLocal);
|
|
info.worldToShape = &m_worldToLocal;
|
|
}
|
|
|
|
info.state = mixed.GetMLRState();
|
|
#if defined(LAB_ONLY)
|
|
if (LODElement::s_ShadeLODs && i<ELEMENTS(LODElement::s_Shades))
|
|
info.paintMeColor = &LODElement::s_Shades[i];
|
|
else
|
|
#endif
|
|
info.paintMeColor = &FadeColor;
|
|
LightList culled_lights;
|
|
info.nrOfActiveLights = CullLights(culled_lights, mixed.m_lights);
|
|
info.activeLights = (info.nrOfActiveLights>0) ? &culled_lights[0] : NULL;
|
|
|
|
//
|
|
//---------------
|
|
// Draw the shape
|
|
//---------------
|
|
//
|
|
MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
|
|
Check_Object(clipper);
|
|
|
|
Stop_Timer(Graph_Traversal);
|
|
ELEMENT_RENDER("Culling::Tree::Overridden");
|
|
Start_Timer(Draw_Shapes);
|
|
clipper->DrawShape(&info);
|
|
Stop_Timer(Draw_Shapes);
|
|
Start_Timer(Graph_Traversal);
|
|
|
|
FadeColor.alpha = fade;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern DWORD Disable_Lights;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
ElementRenderer::TreeElement::CullLights(
|
|
LightList culled_lights,
|
|
const LightList lights
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Set up the destination light array and start copying the lights
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (Disable_Lights)
|
|
return 0;
|
|
|
|
int new_count = 0;
|
|
Stuff::Point3D center(m_worldOBB.localToParent);
|
|
for (int i=0; i<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive && lights[i]; ++i)
|
|
{
|
|
MidLevelRenderer::MLRLight *light = lights[i];
|
|
Check_Object(light);
|
|
Verify(new_count < MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive);
|
|
if (light->GetIntensity() < Stuff::SMALL)
|
|
continue;
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we are copying a light with a range, check it against the bounds
|
|
// of this node and only copy if it would hit the bounds
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (light->IsDerivedFrom(MidLevelRenderer::MLRInfiniteLightWithFalloff::DefaultData))
|
|
{
|
|
MidLevelRenderer::MLRInfiniteLightWithFalloff *bounded_light =
|
|
Cast_Object(MidLevelRenderer::MLRInfiniteLightWithFalloff*, light);
|
|
Stuff::Point3D lc(bounded_light->GetLightToWorldMatrix());
|
|
Stuff::Vector3D diff;
|
|
diff.Subtract(center, lc);
|
|
Stuff::Scalar range =
|
|
m_worldOBB.sphereRadius + bounded_light->GetFalloffFar();
|
|
if (diff.GetLengthSquared() < range*range)
|
|
culled_lights[new_count++] = light;
|
|
}
|
|
|
|
//
|
|
//---------------------------------
|
|
// Otherwise it is automatically in
|
|
//---------------------------------
|
|
//
|
|
else
|
|
culled_lights[new_count++] = light;
|
|
if (new_count >= ShapeElement::s_MaxLightsPerShape)
|
|
break;
|
|
}
|
|
Verify(new_count <= MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive);
|
|
return new_count;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
ElementRenderer::TreeElement::CountTriangles()
|
|
{
|
|
Check_Object(this);
|
|
|
|
if (m_MLRShapes.GetLength() > 0)
|
|
{
|
|
Check_Object(m_MLRShapes[0]);
|
|
return m_MLRShapes[0]->GetNumPrimitives();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::TreeElement::CleanDamage()
|
|
{
|
|
Check_Object(this);
|
|
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Check_Object(m_MLRShapes[i]);
|
|
Verify(m_MLRShapes[i]->GetReferenceCount() == 1);
|
|
m_MLRShapes[i]->CleanMe();
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::TreeElement::ApplyDamage(
|
|
const Stuff::LinearMatrix4D &damage_spot,
|
|
Stuff::Scalar radius
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&damage_spot);
|
|
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Check_Object(m_MLRShapes[i]);
|
|
Verify(m_MLRShapes[i]->GetReferenceCount() == 1);
|
|
m_MLRShapes[i]->HurtMe(damage_spot, radius);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::TreeElement::ApplyDamageDecal(
|
|
const Stuff::LinearMatrix4D &damage_spot,
|
|
Stuff::Scalar radius,
|
|
MidLevelRenderer::MLRTexture *texture
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&damage_spot);
|
|
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Check_Object(m_MLRShapes[i]);
|
|
Verify(m_MLRShapes[i]->GetReferenceCount() == 1);
|
|
m_MLRShapes[i]->HurtMe(damage_spot, radius, texture);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::TreeElement::CastShadow(
|
|
const Stuff::LinearMatrix4D &shadow_to_world,
|
|
const Stuff::UnitVector3D &sun_in_world,
|
|
Stuff::Scalar radius
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
if (!IsShadowed())
|
|
return;
|
|
MidLevelRenderer::MLRShape *mlr_shape;
|
|
const Entry *entry;
|
|
Verify(GetSize()>0);
|
|
GetLOD(static_cast<WORD>(GetSize()-1), &mlr_shape, &entry);
|
|
Stuff::LinearMatrix4D world_to_object;
|
|
world_to_object.Invert(GetLocalToWorld());
|
|
Stuff::UnitVector3D sun_in_object;
|
|
sun_in_object.Multiply(sun_in_world, world_to_object);
|
|
Stuff::LinearMatrix4D shadow_to_object;
|
|
shadow_to_object.Multiply(shadow_to_world, world_to_object);
|
|
|
|
mlr_shape->DrawMyShadow(shadow_to_object, sun_in_object, radius);
|
|
mlr_shape->DetachReference();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::TreeElement::MakeFootStep(
|
|
MidLevelRenderer::MLRShape *shape,
|
|
const Stuff::LinearMatrix4D &foot,
|
|
Stuff::Scalar radius,
|
|
MidLevelRenderer::MLRTexture *tex
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
MidLevelRenderer::MLRShape *mlr_shape;
|
|
const Entry *entry;
|
|
|
|
Verify(GetSize()>0);
|
|
GetLOD(static_cast<WORD>(GetSize()-1), &mlr_shape, &entry);
|
|
|
|
Stuff::LinearMatrix4D world_to_object;
|
|
world_to_object.Invert(GetLocalToWorld());
|
|
|
|
Stuff::LinearMatrix4D foot_to_object;
|
|
foot_to_object.Multiply(foot, world_to_object);
|
|
|
|
mlr_shape->StepOnMe(shape, foot_to_object, radius, tex);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
MidLevelRenderer::MLRShape*
|
|
ElementRenderer::TreeElement::GetMLRShape(ElementRenderer::CameraElement* cameraElement)
|
|
{
|
|
Check_Object(this);
|
|
|
|
unsigned short lod = 0;
|
|
if (cameraElement)
|
|
{
|
|
Stuff::Point3D camera_origin(cameraElement->GetLocalToWorld());
|
|
Stuff::Point3D lod_origin(GetLocalToWorld());
|
|
camera_origin -= lod_origin;
|
|
Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset;
|
|
Min_Clamp(distance, 0.0f);
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Search the lod entries looking for the first match
|
|
//---------------------------------------------------
|
|
//
|
|
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
|
|
{
|
|
Entry *entry = &m_ranges[i];
|
|
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
|
|
{
|
|
lod = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MidLevelRenderer::MLRShape *mlr_shape;
|
|
const Entry* ranges;
|
|
|
|
Verify(lod < GetSize());
|
|
GetLOD(lod, &mlr_shape, &ranges);
|
|
mlr_shape->DetachReference();
|
|
|
|
return mlr_shape;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Returns array of vertex coordinates and number of vertices
|
|
void
|
|
ElementRenderer::TreeElement::GetCoordData(
|
|
Stuff::DynamicArrayOf<Stuff::Point3D> *array)
|
|
{
|
|
Check_Object(this);
|
|
|
|
//
|
|
//-----------------------------------
|
|
// First count the number of vertices
|
|
//-----------------------------------
|
|
//
|
|
int total=0;
|
|
int meshes = m_MLRShapes.GetLength();
|
|
int m;
|
|
for(m=0; m<meshes; m++)
|
|
{
|
|
int ns = m_MLRShapes[m]->GetNum();
|
|
for (int n =0;n<ns;n++)
|
|
{
|
|
MidLevelRenderer::MLRPrimitiveBase *mesh = m_MLRShapes[m]->Find(n);
|
|
Check_Object(mesh);
|
|
const Stuff::Point3D *verts;
|
|
int count;
|
|
mesh->GetCoordData(&verts, &count);
|
|
total += count;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------
|
|
// Now allocate the memory
|
|
//------------------------
|
|
//
|
|
array->SetLength(total);
|
|
total = 0;
|
|
for(m=0; m<meshes; m++)
|
|
{
|
|
int ns = m_MLRShapes[m]->GetNum();
|
|
for (int n =0;n<ns;n++)
|
|
{
|
|
MidLevelRenderer::MLRPrimitiveBase *mesh = m_MLRShapes[m]->Find(n);
|
|
Check_Object(mesh);
|
|
const Stuff::Point3D *verts;
|
|
int count;
|
|
mesh->GetCoordData(&verts, &count);
|
|
|
|
for (int v=0; v<count; ++v)
|
|
{
|
|
(*array)[total+v]=verts[v];
|
|
}
|
|
total += count;
|
|
}
|
|
}
|
|
}
|
|
|