Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

1278 lines
36 KiB
C++

#include "ElementRendererHeaders.hpp"
#include <MLR\MLRInfiniteLightWithFalloff.hpp>
//#define BOUNDS_BUG
//############################################################################
//############################ ShapeLODElement ################################
//############################################################################
HGOSHEAP ElementRenderer::ShapeLODElement::s_Heap = NULL;
ElementRenderer::ShapeLODElement::ClassData* ElementRenderer::ShapeLODElement::DefaultData = NULL;
ElementRenderer::Element::SyncMethod ElementRenderer::ShapeLODElement::SyncMethods[SyncStateCount]=
{
//
// Root mode
//
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// Sphere Cull
//
SYNC_METHOD(ShapeLODElement, CleanSphere),
SYNC_METHOD(ShapeLODElement, DirtySphere),
SYNC_METHOD(ShapeLODElement, CleanSphere),
SYNC_METHOD(ShapeLODElement, DirtySphere),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// OBB Cull
//
SYNC_METHOD(ShapeLODElement, CleanOBB),
SYNC_METHOD(ShapeLODElement, DirtyOBB),
SYNC_METHOD(ShapeLODElement, CleanOBB),
SYNC_METHOD(ShapeLODElement, DirtyOBB),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// Never Cull
//
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, Dirty),
SYNC_METHOD(ShapeLODElement, Clean),
SYNC_METHOD(ShapeLODElement, 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::ShapeLODElement::DrawMethods[DrawStateCount]=
{
//
// No billboarding
//
DRAW_METHOD(ShapeLODElement, Inherit),
DRAW_METHOD(ShapeLODElement, Override)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::InitializeClass()
{
Verify(!s_Heap);
s_Heap = gos_CreateMemoryHeap("ShapeLOD", 0, g_LibraryHeap);
Check_Pointer(s_Heap);
Verify(!DefaultData);
DefaultData =
new ClassData(
ShapeLODElementClassID,
"ElementRenderer::ShapeLODElement",
BaseClass::DefaultData,
reinterpret_cast<Factory>(&Make)
);
Check_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::TerminateClass()
{
Check_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
Check_Pointer(s_Heap);
gos_DestroyMemoryHeap(s_Heap);
s_Heap = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::ShapeLODElement::ShapeLODElement(
ClassData *class_data,
Stuff::MemoryStream *stream,
int version,
ShapeHolder shapes
):
Element(class_data, stream, version)
{
Check_Pointer(this);
Check_Object(stream);
if (version<10)
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 >> count;
m_ranges.SetLength(count);
m_MLRShapes.SetLength(count);
int mlr_version = MidLevelRenderer::ReadMLRVersion(stream);
//
//-----------------------------------------------
// For older versions, we create all the LOD info
//-----------------------------------------------
//
if (version < 13)
{
for (WORD i=0; i<count; ++i)
{
RegisteredClass::ClassID class_id;
*stream >> class_id;
switch(class_id)
{
case MidLevelRenderer::MLRShapeClassID:
m_MLRShapes[i] = MidLevelRenderer::MLRShape::Make(stream, mlr_version);
break;
case MidLevelRenderer::MLRCulturShapeClassID:
m_MLRShapes[i] = MidLevelRenderer::MLRCulturShape::Make(stream, mlr_version);
break;
}
Check_Object(m_MLRShapes[i]);
*stream >> m_ranges[i].m_nearSquared >> m_ranges[i].m_farSquared;
}
}
//
//------------------------------------------------------------------------------------
// Newer versions of the data will allow us to skip reading LODs that won't be visible
//------------------------------------------------------------------------------------
//
else
{
WORD used=0;
for (WORD i=0; i<count; ++i)
{
*stream >> m_ranges[used].m_nearSquared >> 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;
//
//-------------------------------------------------------------------
// 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::ShapeLODElement::ShapeLODElement(ClassData *class_data):
Element(class_data)
{
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::ShapeLODElement::~ShapeLODElement()
{
Check_Object(this);
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
{
Check_Object(m_MLRShapes[i]);
m_MLRShapes[i]->DetachReference();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::ShapeLODElement* ElementRenderer::ShapeLODElement::Make(
Stuff::MemoryStream *stream,
int version,
ShapeHolder shapes
)
{
Check_Object(stream);
gos_PushCurrentHeap(s_Heap);
ShapeLODElement *element = new ShapeLODElement(DefaultData, stream, version, shapes);
gos_PopCurrentHeap();
return element;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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 << 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_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::ShapeLODElement::TestInstance()
{
Verify(IsDerivedFrom(DefaultData));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool ElementRenderer::ShapeLODElement::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::ShapeLODElement::AttachChild(Element *child)
{
Check_Object(this);
Check_Object(child);
STOP(("Shape elements can't have children!"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::DetachChild(Element *child)
{
Check_Object(this);
Check_Object(child);
STOP(("Shape elements can't have children!"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::AttachLOD(
WORD index,
MidLevelRenderer::MLRShape *shape,
const LODElement::Entry &ranges
)
{
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] = ranges;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 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::ShapeLODElement::AttachLODFirstAvailableSlot(
MidLevelRenderer::MLRShape *shape,
const LODElement::Entry &ranges
)
{
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] = ranges;
return index;
}
}
STOP(("ShapeLODElement: Can't attach another LOD, already full!"));
return -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::GetLOD(
WORD index,
MidLevelRenderer::MLRShape **shape,
const LODElement::Entry **ranges
)
{
Check_Object(this);
Check_Object(m_MLRShapes[index]);
*shape = m_MLRShapes[index];
(*shape)->AttachReference();
*ranges = &m_ranges[index];
}
void ElementRenderer::ShapeLODElement::SetLOD(
WORD index,
const LODElement::Entry &ranges
)
{
Check_Object(this);
m_ranges[index]=ranges;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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_farSquared = 0.0f;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WORD ElementRenderer::ShapeLODElement::GetSize()
{
Check_Object(this);
Verify(m_MLRShapes.GetLength() < 65536);
return static_cast<WORD>(m_MLRShapes.GetLength());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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::ShapeLODElement::SetDrawState()
{
Check_Object(this);
unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
Verify(index < DrawStateCount);
m_draw = DrawMethods[index];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::DirtySyncMethod()
{
Check_Object(this);
Verify(IsMatrixDirty() && GetCullMode()==NeverCullMode);
SYNC_LOGIC("Dirty::ShapeLOD");
//
//--------------------
// 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::ShapeLODElement::CleanSphereSyncMethod()
{
Check_Object(this);
Verify(m_localOBB.sphereRadius > 0.0f);
Verify(
!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::Clean::ShapeLOD");
//
//-------------------------------
// 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::ShapeLODElement::DirtySphereSyncMethod()
{
Check_Object(this);
Verify(m_localOBB.sphereRadius > 0.0f);
Verify(
IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::Dirty::ShapeLOD");
//
//--------------------
// 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::ShapeLODElement::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::ShapeLOD");
//
//-------------------------------
// 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::ShapeLODElement::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::ShapeLOD");
//
//--------------------
// 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::ShapeLODElement::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::ShapeLODElement::FindSmallestElementContainingCulled(SphereTest *test)
{
Check_Object(this);
Check_Object(test);
return this;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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);
//
//---------------------------------------------------
// Search the lod entries looking for the first match
//---------------------------------------------------
//
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
{
LODElement::Entry *entry = &m_ranges[i];
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
{
//
//----------------------------
// Set up the draw information
//----------------------------
//
MidLevelRenderer::DrawShapeInformation info;
Check_Object(m_MLRShapes[i]);
info.shape = m_MLRShapes[i];
info.clippingFlags.SetClippingState(culling_state);
Check_Object(&m_localToWorld);
info.shapeToWorld = &m_localToWorld;
Check_Object(&m_worldToLocal);
info.worldToShape = &m_worldToLocal;
info.state = inherited_state->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::ShapeLOD::Inherited");
Start_Timer(Draw_Shapes);
clipper->DrawShape(&info);
Stop_Timer(Draw_Shapes);
Start_Timer(Graph_Traversal);
break;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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);
//
//---------------------------------------------------
// Search the lod entries looking for the first match
//---------------------------------------------------
//
for (WORD i=0; i<m_MLRShapes.GetLength(); ++i)
{
LODElement::Entry *entry = &m_ranges[i];
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
{
//
//----------------------------
// Set up the draw information
//----------------------------
//
MidLevelRenderer::DrawShapeInformation info;
Check_Object(m_MLRShapes[i]);
info.shape = m_MLRShapes[i];
info.clippingFlags.SetClippingState(culling_state);
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::ShapeLOD::Overridden");
Start_Timer(Draw_Shapes);
clipper->DrawShape(&info);
Stop_Timer(Draw_Shapes);
Start_Timer(Graph_Traversal);
break;
}
}
}
extern DWORD Disable_Lights;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int ElementRenderer::ShapeLODElement::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;
ELEMENT_RENDER("Culling::ShapeLOD::Lights");
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::ShapeLODElement::CountTriangles()
{
Check_Object(this);
if (m_MLRShapes.GetLength() > 0)
{
Check_Object(m_MLRShapes[0]);
return m_MLRShapes[0]->GetNumPrimitives();
}
return 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ElementRenderer::ShapeLODElement::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::ShapeLODElement::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::ShapeLODElement::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::ShapeLODElement::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 ElementRenderer::LODElement::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::ShapeLODElement::MakeFootStep(
MidLevelRenderer::MLRShape *shape,
const Stuff::LinearMatrix4D &foot,
Stuff::Scalar radius,
MidLevelRenderer::MLRTexture *tex
)
{
Check_Object(this);
MidLevelRenderer::MLRShape *mlr_shape;
const ElementRenderer::LODElement::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::ShapeLODElement::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)
{
LODElement::Entry *entry = &m_ranges[i];
if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance)
{
lod = i;
break;
}
}
}
MidLevelRenderer::MLRShape *mlr_shape;
const ElementRenderer::LODElement::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::ShapeLODElement::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;
}
}
}