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

1382 lines
36 KiB
C++

#include "ElementRendererHeaders.hpp"
#include "GroupElement.hpp"
//#define BOUNDS_BUG "jmalbert"
#if defined(BOUNDS_BUG)
int prefix_len=0;
#endif
//############################################################################
//############################ GroupElement ################################
//############################################################################
HGOSHEAP
ElementRenderer::GroupElement::s_Heap = NULL;
ElementRenderer::GroupElement::ClassData*
ElementRenderer::GroupElement::DefaultData = NULL;
ElementRenderer::Element::SyncMethod
ElementRenderer::GroupElement::SyncMethods[SyncStateCount]=
{
//
// Root mode
//
SYNC_METHOD(GroupElement, CleanRoot),
SYNC_METHOD(GroupElement, DirtyRoot),
SYNC_METHOD(GroupElement, CleanRoot),
SYNC_METHOD(GroupElement, DirtyRoot),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(GroupElement, CleanRoot),
SYNC_METHOD(GroupElement, DirtyRoot),
SYNC_METHOD(GroupElement, CleanRoot),
SYNC_METHOD(GroupElement, DirtyRoot),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// Stuff::Sphere Cull
//
SYNC_METHOD(GroupElement, CleanSphere),
SYNC_METHOD(GroupElement, MatrixDirtySphere),
SYNC_METHOD(GroupElement, BoundsWrongSphere),
SYNC_METHOD(GroupElement, FullSphere),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// OBB Cull
//
SYNC_METHOD(GroupElement, CleanOBB),
SYNC_METHOD(GroupElement, MatrixDirtyOBB),
SYNC_METHOD(GroupElement, BoundsWrongOBB),
SYNC_METHOD(GroupElement, FullOBB),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
//
// Never Cull
//
SYNC_METHOD(GroupElement, Clean),
SYNC_METHOD(GroupElement, Dirty),
SYNC_METHOD(GroupElement, Clean),
SYNC_METHOD(GroupElement, Dirty),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(Element, None),
SYNC_METHOD(Element, Defer),
SYNC_METHOD(GroupElement, Clean),
SYNC_METHOD(GroupElement, Dirty),
SYNC_METHOD(GroupElement, Clean),
SYNC_METHOD(GroupElement, 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::GroupElement::DrawMethods[DrawStateCount]=
{
DRAW_METHOD(GroupElement, Inherit),
DRAW_METHOD(GroupElement, Override)
};
static DWORD
Wasted_Syncs;
#if !defined(NO_STATS)
#define WASTE_CHECK()\
if (Stuff::ArmorLevel>0 && !children.GetCurrent())\
Set_Statistic(Wasted_Syncs, Wasted_Syncs+1);
#else
#define WASTE_CHECK()
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::InitializeClass()
{
Verify(!s_Heap);
s_Heap = gos_CreateMemoryHeap("Group", 0, g_LibraryHeap);
Check_Pointer(s_Heap);
Verify(!DefaultData);
DefaultData =
new ClassData(
GroupElementClassID,
"ElementRenderer::GroupElement",
BaseClass::DefaultData,
reinterpret_cast<Factory>(&Make)
);
Register_Object(DefaultData);
Wasted_Syncs = 0;
#if !defined(NO_STATS)
AddStatistic("Wasted Group Syncs", "syncs", gos_DWORD, &Wasted_Syncs, Stat_AutoReset);
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
Check_Pointer(s_Heap);
gos_DestroyMemoryHeap(s_Heap);
s_Heap = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GroupElement::GroupElement(
ClassData *class_data,
Stuff::MemoryStream *stream,
int version,
ShapeHolder shapes
):
Element(class_data, stream, version),
m_group(NULL)
{
Check_Pointer(this);
Check_Object(stream);
Check_Object(class_data);
//
//------------------
// Update the tables
//------------------
//
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];
//
//----------------------------
// Do the child creation thing
//----------------------------
//
int count;
*stream >> count;
while (count--)
{
Element *child = Create(stream, version);
Check_Object(child);
AttachChild(child);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GroupElement::GroupElement(ClassData *class_data):
Element(class_data),
m_group(NULL)
{
Check_Pointer(this);
Check_Object(class_data);
//
//------------------
// Update the tables
//------------------
//
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];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GroupElement::~GroupElement()
{
Check_Object(this);
//
//------------------------
// Detach all the children
//------------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Unregister_Object(child);
delete child;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GroupElement*
ElementRenderer::GroupElement::Make(
Stuff::MemoryStream *stream,
int version,
ShapeHolder shapes
)
{
Check_Object(stream);
gos_PushCurrentHeap(s_Heap);
GroupElement *element = new GroupElement(DefaultData, stream, version, shapes);
gos_PopCurrentHeap();
return element;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::Save(Stuff::MemoryStream *stream)
{
Check_Object(this);
Check_Object(stream);
BaseClass::Save(stream);
//
//-----------------------------------------------------------
// Write out the child count and then the children themselves
//-----------------------------------------------------------
//
Stuff::ChainIteratorOf<Element*> elements(&m_group);
Element *element;
*stream << elements.GetSize();
while ((element = elements.ReadAndNext()) != NULL)
{
Check_Object(element);
element->Save(stream);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::TestInstance()
{
Verify(IsDerivedFrom(DefaultData));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::AttachChild(Element *child)
{
Check_Object(this);
Check_Object(child);
//
//--------------------------------------------------------------------
// Increment the child's reference count, and add it to our child list
//--------------------------------------------------------------------
//
Verify(!child->GetParentElement());
Verify(!m_group.IsPlugMember(child));
m_group.Add(child);
SetParentElement(child, this);
if (!AreBoundsLocked())
NeedNewBounds();
if (child->GetCullMode() == RootMode)
{
if (child->m_localOBB.sphereRadius > 0.0f || child->AreBoundsWrong())
child->SetVolumeCullMode();
else
child->SetNeverCullMode();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::DetachChild(Element *child)
{
Check_Object(this);
Check_Object(child);
//
//-------------------------------------------------------------------
// Decrement the child's reference count, and remove it from our list
//-------------------------------------------------------------------
//
Verify(child->GetParentElement() == this);
m_group.RemovePlug(child);
SetParentElement(child, NULL);
child->SetRootMode();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::DetachChildren()
{
Check_Object(this);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Verify(child->GetParentElement() == this);
m_group.RemovePlug(child);
SetParentElement(child, NULL);
child->SetRootMode();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::SetSyncState()
{
Check_Object(this);
/*
Verify(
GetCullMode() != VolumeCullMode
|| AreBoundsWrong()
|| IsSyncDeferred()
|| (
m_localOBB.sphereRadius>0.0f
&& (IsMatrixDirty() || m_worldOBB.sphereRadius>0.0f
)
)
);
*/
Verify(
GetCullMode() == RootMode
|| GetCullMode() == AlwaysCullMode
|| m_parent != NULL
|| AreBoundsWrong()
);
unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
Verify(index < SyncStateCount);
m_sync = SyncMethods[index];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::SetDrawState()
{
Check_Object(this);
unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
Verify(index < DrawStateCount);
m_draw = DrawMethods[index];
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::CleanRootSyncMethod()
{
Check_Object(this);
Verify(!IsMatrixDirty() && GetCullMode() == RootMode);
SYNC_LOGIC("Root::Clean::Group");
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::DirtyRootSyncMethod()
{
Check_Object(this);
Verify(IsMatrixDirty() && GetCullMode() == RootMode);
SYNC_LOGIC("Root::Dirty::Group");
//
//------------------------------------------------------
// If the local-to-parent matrix is dirty, make it clean
//------------------------------------------------------
//
m_localToWorld = m_localToParent = m_newLocalToParent;
MatrixIsClean();
CleanRootSyncMethod();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::CleanSyncMethod()
{
Check_Object(this);
Verify(!IsMatrixDirty() && GetCullMode() == NeverCullMode);
SYNC_LOGIC("Clean::Group");
//
//------------------------------------------------------------------
// Concatenated our m_localToWorld from m_localToParent and our parent's
// m_localToWorld
//------------------------------------------------------------------
//
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
else
m_localToWorld = m_parent->GetLocalToWorld();
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::DirtySyncMethod()
{
Check_Object(this);
Verify(IsMatrixDirty() && GetCullMode() == NeverCullMode);
SYNC_LOGIC("Dirty::Group");
//
//------------------------------------------------------
// If the local-to-parent matrix is dirty, make it clean
//------------------------------------------------------
//
m_localToParent = m_newLocalToParent;
//
//------------------------------------------------------------------
// Concatenated our m_localToWorld from m_localToParent and our parent's
// m_localToWorld
//------------------------------------------------------------------
//
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
else
m_localToWorld = m_parent->GetLocalToWorld();
MatrixIsClean();
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::CleanSphereSyncMethod()
{
Check_Object(this);
Verify(m_localOBB.sphereRadius > 0.0f);
Verify(
!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::Clean::Group");
//
//------------------------------------------------------------------
// Concatenated our m_localToWorld from m_localToParent and our parent's
// m_localToWorld
//------------------------------------------------------------------
//
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
else
m_localToWorld = m_parent->GetLocalToWorld();
//
//------------------------------
// Transform the bounding sphere
//------------------------------
//
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::MatrixDirtySphereSyncMethod()
{
Check_Object(this);
Verify(m_localOBB.sphereRadius > 0.0f);
Verify(
IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::Dirty::Group");
//
//--------------------
// Update the matrices
//--------------------
//
m_localToParent = m_newLocalToParent;
Check_Object(&m_localToParent);
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
{
Check_Object(&m_parent->GetLocalToWorld());
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
Check_Object(&m_localToWorld);
}
else
{
m_localToWorld = m_parent->GetLocalToWorld();
Check_Object(&m_localToWorld);
}
//
//--------------------------------------------------------------
// Transform the bounding sphere, then mark the element as clean
//--------------------------------------------------------------
//
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
MatrixIsClean();
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::BoundsWrongSphereSyncMethod()
{
Check_Object(this);
Verify(
!IsMatrixDirty() && AreBoundsWrong()
&& GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::RecalcBounds::Group");
//
//------------------------------------------------------------------
// Concatenated our m_localToWorld from m_localToParent and our parent's
// m_localToWorld
//------------------------------------------------------------------
//
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
else
m_localToWorld = m_parent->GetLocalToWorld();
//
//------------------------------------------------------------------------
// If our bounds are locked, just run the children and transform our local
// bounds
//------------------------------------------------------------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
if (AreBoundsLocked())
{
Verify(m_localOBB.sphereRadius > 0.0f);
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
BoundsAreRight();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
return;
}
//
//-----------------------------------------------------
// Update our children, then get their bounding spheres
//-----------------------------------------------------
//
#if defined(BOUNDS_BUG)
for (int i=0; i<prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
++prefix_len;
SPEW((BOUNDS_BUG, "%s = %x", GetClassData()->GetClassName(), this));
#endif
Stuff::Sphere bounds(Stuff::Point3D::Identity, 0.0f);
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
if (child->GetCullMode() == VolumeCullMode)
{
Stuff::LinearMatrix4D child_to_parent = child->GetLocalToParent();
Stuff::Point3D child_center(child->m_localOBB.localToParent);
//
//--------------------------------------------------------------
// If we are derived from a group, we can dynamically move so we
// need to expand the bounding volume
//--------------------------------------------------------------
//
#if defined(BOUNDS_BUG)
for (int i=0; i<prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
#endif
Stuff::Sphere child_sphere;
if (child->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData))
{
child_sphere.center = child_to_parent;
child_sphere.radius =
child_center.GetLength() + child->m_localOBB.sphereRadius;
#if defined(BOUNDS_BUG)
SPEW((BOUNDS_BUG, "%s %x = +", child->GetClassData()->GetClassName(), child));
Spew(BOUNDS_BUG, child_sphere.center);
SPEW((
BOUNDS_BUG,
", %f(%f+%f)",
child_sphere.radius,
child_center.GetLength(),
child->m_localOBB.sphereRadius
));
#endif
}
//
//-------------------------------------------------------------
// If not, the object is static, so just deal with the centroid
//-------------------------------------------------------------
//
else
{
child_sphere.center.Multiply(child_center, child_to_parent);
child_sphere.radius = child->m_localOBB.sphereRadius;
#if defined(BOUNDS_BUG)
SPEW((BOUNDS_BUG, "%s %x = +", child->GetClassData()->GetClassName(), child));
Spew(BOUNDS_BUG, child_sphere.center);
SPEW((BOUNDS_BUG, ",%f", child_sphere.radius));
#endif
}
//
//---------------------
// Build the new sphere
//---------------------
//
if (bounds.radius == 0.0f)
bounds = child_sphere;
else
bounds.Union(bounds, child_sphere);
}
}
#if defined(BOUNDS_BUG)
--prefix_len;
for (i=0; i<prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
SPEW((BOUNDS_BUG, "Bounds = +"));
Spew(BOUNDS_BUG, bounds.center);
SPEW((BOUNDS_BUG, ",%f", bounds.radius));
#endif
m_localOBB.BuildSphere(bounds);
//
//-----------------------------------------------------------------------
// If we have no bounded children, we will never cull, otherwise take the
// local bounds into world space
//-----------------------------------------------------------------------
//
if (m_localOBB.sphereRadius == 0.0f)
SetNeverCullMode();
else
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
BoundsAreRight();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::FullSphereSyncMethod()
{
Check_Object(this);
Verify(
IsMatrixDirty() && AreBoundsWrong()
&& GetCullMode()==VolumeCullMode && IsBoundedBySphere()
);
SYNC_LOGIC("Sphere::Full::Group");
//
//--------------------
// Update the 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();
//
//------------------------------------------------------------------------
// If our bounds are locked, just run the children and transform our local
// bounds
//------------------------------------------------------------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
if (AreBoundsLocked())
{
Verify(m_localOBB.sphereRadius > 0.0f);
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
m_state &= ~(MatrixDirtyFlag|BoundsWrongFlag);
SetSyncState();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
return;
}
//
//-----------------------------------------------------
// Update our children, then get their bounding spheres
//-----------------------------------------------------
//
#if defined(BOUNDS_BUG)
if (!prefix_len)
SPEW((BOUNDS_BUG, "%s = %x", GetClassData()->GetClassName(), this));
++prefix_len;
#endif
Stuff::Sphere bounds(Stuff::Point3D::Identity, 0.0f);
#if defined(BOUNDS_BUG)
int child_count = 0;
#endif
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
#if defined(BOUNDS_BUG)
for (int i=0; i<prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
SPEW((BOUNDS_BUG, "%d: syncing %s(%x)", child_count, child->GetClassData()->GetClassName(), child));
#endif
child->Sync();
#if defined(BOUNDS_BUG)
for (i=0; i<=prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
#endif
if (child->GetCullMode() == VolumeCullMode)
{
Stuff::LinearMatrix4D child_to_parent = child->GetLocalToParent();
Stuff::Point3D child_center(child->m_localOBB.localToParent);
//
//--------------------------------------------------------------
// If we are derived from a group, we can dynamically move so we
// need to expand the bounding volume
//--------------------------------------------------------------
//
Stuff::Sphere child_sphere;
if (child->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData))
{
child_sphere.center = child_to_parent;
child_sphere.radius =
child_center.GetLength() + child->m_localOBB.sphereRadius;
#if defined(BOUNDS_BUG)
Spew(BOUNDS_BUG, child_sphere.center);
SPEW((
BOUNDS_BUG, ", %f(%f+%f)",
child_sphere.radius,
child_center.GetLength(),
child->m_localOBB.sphereRadius
));
#endif
}
//
//-------------------------------------------------------------
// If not, the object is static, so just deal with the centroid
//-------------------------------------------------------------
//
else
{
child_sphere.center.Multiply(child_center, child_to_parent);
child_sphere.radius = child->m_localOBB.sphereRadius;
#if defined(BOUNDS_BUG)
Spew(BOUNDS_BUG, child_sphere.center);
SPEW((BOUNDS_BUG, ", %f", child_sphere.radius));
#endif
}
//
//---------------------
// Build the new sphere
//---------------------
//
if (bounds.radius == 0.0f)
bounds = child_sphere;
else
bounds.Union(bounds, child_sphere);
}
#if defined(BOUNDS_BUG)
else
SPEW((BOUNDS_BUG, " isn't volume culled"));
++child_count;
#endif
}
#if defined(BOUNDS_BUG)
--prefix_len;
for (int i=0; i<prefix_len; ++i)
SPEW((BOUNDS_BUG, " +"));
SPEW((BOUNDS_BUG, "%d children, Bounds = +", child_count));
Spew(BOUNDS_BUG, bounds.center);
SPEW((BOUNDS_BUG, ",%f", bounds.radius));
#endif
m_localOBB.BuildSphere(bounds);
//
//-----------------------------------------------------------------------
// If we have no bounded children, we will never cull, otherwise take the
// local bounds into world space
//-----------------------------------------------------------------------
//
if (m_localOBB.sphereRadius == 0.0f)
SetNeverCullMode();
else
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
m_state &= ~(MatrixDirtyFlag|BoundsWrongFlag);
SetSyncState();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::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::Group");
//
//------------------------------------------------------------------
// Concatenated our m_localToWorld from m_localToParent and our parent's
// m_localToWorld
//------------------------------------------------------------------
//
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
else
m_localToWorld = m_parent->GetLocalToWorld();
//
//------------------------------
// Transform the bounding sphere
//------------------------------
//
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::MatrixDirtyOBBSyncMethod()
{
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::Group");
//
//--------------------
// Update the matrices
//--------------------
//
m_localToParent = m_newLocalToParent;
Check_Object(&m_localToParent);
Check_Object(m_parent);
if (!IsLocalToParentIdentity())
{
Check_Object(&m_parent->GetLocalToWorld());
m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
Check_Object(&m_localToWorld);
}
else
{
m_localToWorld = m_parent->GetLocalToWorld();
Check_Object(&m_localToWorld);
}
//
//--------------------------------------------------------------
// Transform the bounding sphere, then mark the element as clean
//--------------------------------------------------------------
//
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
MatrixIsClean();
//
//--------------------
// Update our children
//--------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
WASTE_CHECK();
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->Sync();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::BoundsWrongOBBSyncMethod()
{
Check_Object(this);
STOP(("OBB's cannot recalculate bounds!"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::FullOBBSyncMethod()
{
Check_Object(this);
STOP(("OBB's cannot recalculate bounds!"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
ElementRenderer::GroupElement::CastCulledRay(CollisionQuery *query)
{
Check_Object(this);
Check_Object(query);
Verify(!query->m_data);
//
//------------------
// Test our children
//------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
bool result = false;
void *client_data = m_data;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->CastRay(query))
{
result = true;
if (query->m_data)
{
client_data = query->m_data;
query->m_data = NULL;
}
}
}
//
//-----------------------------------------------------------------------
// If we hit something, set the client data if it hasn't already been set
//-----------------------------------------------------------------------
//
if (result)
query->m_data = client_data;
else
Verify(!query->m_data);
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::Element*
ElementRenderer::GroupElement::FindSmallestElementContainingCulled(SphereTest *test)
{
Check_Object(this);
Check_Object(test);
//
//------------------
// Draw our children
//------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
Element *result = this;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
Element *temp = child->FindSmallestElementContaining(test);
if (temp)
result = temp;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::InheritDrawMethod(
CameraElement *camera,
const StateChange *inherited_state,
int culling_state
)
{
Check_Object(this);
Check_Object(inherited_state);
Check_Object(camera);
Verify(culling_state != -1);
//
//--------------------------------------
// 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);
}
#endif
//
//------------------
// Draw our children
//------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
camera->DrawElement(child, inherited_state);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::OverrideDrawMethod(
CameraElement *camera,
const StateChange *inherited_state,
int culling_state
)
{
Check_Object(this);
Check_Object(inherited_state);
Check_Object(camera);
Verify(culling_state != -1);
//
//-------------------
// 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, inherited_state, culling_state, this);
}
#endif
//
//------------------
// Draw our children
//------------------
//
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
camera->DrawElement(child, &mixed);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::CleanDamage()
{
Check_Object(this);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child = NULL;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->CleanDamage();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::ApplyDamage(
const Stuff::LinearMatrix4D &damage_spot,
Stuff::Scalar radius
)
{
Check_Object(this);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child = NULL;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->ApplyDamage(damage_spot, radius);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::ApplyDamageDecal(
const Stuff::LinearMatrix4D &damage_spot,
Stuff::Scalar radius,
MidLevelRenderer::MLRTexture *texture
)
{
Check_Object(this);
Check_Object(texture);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child = NULL;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->ApplyDamageDecal(damage_spot, radius, texture);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::CastShadow(
const Stuff::LinearMatrix4D &shadow_to_world,
const Stuff::UnitVector3D &sun_in_world,
Stuff::Scalar radius
)
{
Check_Object(this);
if (!IsShadowed())
return;
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child = NULL;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->CastShadow(shadow_to_world, sun_in_world, radius);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ElementRenderer::GroupElement::MakeFootStep(
MidLevelRenderer::MLRShape *shape,
const Stuff::LinearMatrix4D &foot,
Stuff::Scalar radius,
MidLevelRenderer::MLRTexture *tex
)
{
Check_Object(this);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child = NULL;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
child->MakeFootStep(shape, foot, radius, tex);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
ElementRenderer::GroupElement::CountTriangles()
{
Check_Object(this);
//
//------------------
// Draw our children
//------------------
//
int result = 0;
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
result += child->CountTriangles();
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Returns array of vertex coordinates and number of vertices
void
ElementRenderer::GroupElement::GetCoordData(
Stuff::DynamicArrayOf<Stuff::Point3D> *array)
{
Check_Object(this);
Stuff::ChainIteratorOf<Element*> children(&m_group);
Element *child;
while ((child = children.ReadAndNext()) != NULL)
{
Stuff::DynamicArrayOf<Stuff::Point3D> child_array;
child->GetCoordData(&child_array);
int point_count = array->GetLength();
array->SetLength(point_count+child_array.GetLength());
for (int v=0; v<child_array.GetLength(); ++v)
{
(*array)[point_count+v]=child_array[v];
}
}
}