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.
1248 lines
30 KiB
C++
1248 lines
30 KiB
C++
#include "ElementRendererHeaders.hpp"
|
|
|
|
//############################################################################
|
|
//############################ SwitchElement ################################
|
|
//############################################################################
|
|
|
|
HGOSHEAP
|
|
ElementRenderer::SwitchElement::s_Heap = NULL;
|
|
|
|
ElementRenderer::SwitchElement::ClassData*
|
|
ElementRenderer::SwitchElement::DefaultData = NULL;
|
|
|
|
ElementRenderer::Element::SyncMethod
|
|
ElementRenderer::SwitchElement::SyncMethods[SyncStateCount]=
|
|
{
|
|
//
|
|
// Root mode
|
|
//
|
|
SYNC_METHOD(SwitchElement, CleanRoot),
|
|
SYNC_METHOD(SwitchElement, DirtyRoot),
|
|
SYNC_METHOD(SwitchElement, CleanRoot),
|
|
SYNC_METHOD(SwitchElement, DirtyRoot),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
SYNC_METHOD(SwitchElement, CleanRoot),
|
|
SYNC_METHOD(SwitchElement, DirtyRoot),
|
|
SYNC_METHOD(SwitchElement, CleanRoot),
|
|
SYNC_METHOD(SwitchElement, DirtyRoot),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// Sphere Cull
|
|
//
|
|
SYNC_METHOD(SwitchElement, CleanSphere),
|
|
SYNC_METHOD(SwitchElement, MatrixDirtySphere),
|
|
SYNC_METHOD(SwitchElement, BoundsWrongSphere),
|
|
SYNC_METHOD(SwitchElement, FullSphere),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// OBB Cull
|
|
//
|
|
SYNC_METHOD(SwitchElement, CleanOBB),
|
|
SYNC_METHOD(SwitchElement, MatrixDirtyOBB),
|
|
SYNC_METHOD(SwitchElement, BoundsWrongOBB),
|
|
SYNC_METHOD(SwitchElement, FullOBB),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
//
|
|
// Never Cull
|
|
//
|
|
SYNC_METHOD(SwitchElement, Clean),
|
|
SYNC_METHOD(SwitchElement, Dirty),
|
|
SYNC_METHOD(SwitchElement, Clean),
|
|
SYNC_METHOD(SwitchElement, Dirty),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
SYNC_METHOD(Element, None),
|
|
SYNC_METHOD(Element, Defer),
|
|
|
|
SYNC_METHOD(SwitchElement, Clean),
|
|
SYNC_METHOD(SwitchElement, Dirty),
|
|
SYNC_METHOD(SwitchElement, Clean),
|
|
SYNC_METHOD(SwitchElement, 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::SwitchElement::DrawMethods[DrawStateCount]=
|
|
{
|
|
DRAW_METHOD(SwitchElement, Inherit),
|
|
DRAW_METHOD(SwitchElement, Override),
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::InitializeClass()
|
|
{
|
|
Verify(!s_Heap);
|
|
s_Heap = gos_CreateMemoryHeap("Switch", 0, g_LibraryHeap);
|
|
Check_Pointer(s_Heap);
|
|
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
SwitchElementClassID,
|
|
"ElementRenderer::SwitchElement",
|
|
BaseClass::DefaultData,
|
|
reinterpret_cast<Factory>(&Make)
|
|
);
|
|
Register_Object(DefaultData);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
|
|
Check_Pointer(s_Heap);
|
|
gos_DestroyMemoryHeap(s_Heap);
|
|
s_Heap = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::SwitchElement::SwitchElement(
|
|
ClassData *class_data,
|
|
Stuff::MemoryStream *stream,
|
|
int version,
|
|
ShapeHolder shapes
|
|
):
|
|
ListElement(class_data, stream, version, shapes)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(stream);
|
|
|
|
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];
|
|
|
|
//
|
|
//---------------------------------
|
|
// Create the shape from the stream
|
|
//---------------------------------
|
|
//
|
|
Verify(m_list.GetLength() < 65536);
|
|
*stream >> m_switch;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::SwitchElement::SwitchElement(ClassData *class_data):
|
|
ListElement(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_switch = 0;
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::SwitchElement::~SwitchElement()
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::SwitchElement*
|
|
ElementRenderer::SwitchElement::Make(
|
|
Stuff::MemoryStream *stream,
|
|
int version,
|
|
ShapeHolder shapes
|
|
)
|
|
{
|
|
Check_Object(stream);
|
|
|
|
gos_PushCurrentHeap(s_Heap);
|
|
SwitchElement *element = new SwitchElement(DefaultData, stream, version, shapes);
|
|
gos_PopCurrentHeap();
|
|
|
|
return element;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::Save(Stuff::MemoryStream *stream)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
BaseClass::Save(stream);
|
|
|
|
//
|
|
//--------------------
|
|
// Save each Switch entry
|
|
//--------------------
|
|
//
|
|
*stream << m_switch;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::TestInstance()
|
|
{
|
|
Verify(!AreBoundsLocked());
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::SetSize(WORD max_size)
|
|
{
|
|
Check_Object(this);
|
|
BaseClass::SetSize(max_size);
|
|
m_switch = max_size;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::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::SwitchElement::SetDrawState()
|
|
{
|
|
Check_Object(this);
|
|
unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
|
|
Verify(index < DrawStateCount);
|
|
m_draw = DrawMethods[index];
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::CleanRootSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(!IsMatrixDirty() && GetCullMode()==RootMode);
|
|
SYNC_LOGIC("Root::Clean::Switch");
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::DirtyRootSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(IsMatrixDirty() && GetCullMode()==RootMode);
|
|
SYNC_LOGIC("Root::Dirty::Switch");
|
|
|
|
//
|
|
//------------------------------------------------------
|
|
// If the local-to-parent matrix is dirty, make it clean
|
|
//------------------------------------------------------
|
|
//
|
|
m_localToWorld = m_localToParent = m_newLocalToParent;
|
|
MatrixIsClean();
|
|
CleanRootSyncMethod();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::CleanSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(!IsMatrixDirty() && GetCullMode()==NeverCullMode);
|
|
SYNC_LOGIC("Clean::Switch");
|
|
|
|
//
|
|
//-------------------------------
|
|
// 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();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::DirtySyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(IsMatrixDirty() && GetCullMode()==NeverCullMode);
|
|
SYNC_LOGIC("Dirty::Switch");
|
|
|
|
//
|
|
//--------------------
|
|
// 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();
|
|
MatrixIsClean();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::CleanSphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
Verify(
|
|
!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Clean::Switch");
|
|
|
|
//
|
|
//-------------------------------
|
|
// 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();
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::MatrixDirtySphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
Verify(
|
|
IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Dirty::Switch");
|
|
|
|
//
|
|
//--------------------
|
|
// 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();
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
MatrixIsClean();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::BoundsWrongSphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(
|
|
!IsMatrixDirty() && AreBoundsWrong()
|
|
&& GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Recalc::Switch");
|
|
|
|
//
|
|
//-------------------------------
|
|
// 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();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
WORD i;
|
|
Verify(m_list.GetLength() < 65536);
|
|
WORD switch_count = static_cast<WORD>(m_list.GetLength());
|
|
Stuff::Point3D centroid = Stuff::Point3D::Identity;
|
|
WORD bounded_children = 0;
|
|
for (i=0; i<switch_count; ++i)
|
|
{
|
|
Element *element = m_list[i];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
if (element->GetCullMode() == VolumeCullMode)
|
|
{
|
|
++bounded_children;
|
|
Stuff::LinearMatrix4D child_to_parent = element->GetLocalToParent();
|
|
Stuff::Point3D child_center(element->m_localOBB.localToParent);
|
|
Stuff::Point3D child_center_in_parent;
|
|
child_center_in_parent.Multiply(child_center, child_to_parent);
|
|
centroid += child_center_in_parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// If we have no bounded children, we will never cull
|
|
//---------------------------------------------------
|
|
//
|
|
if (!bounded_children)
|
|
{
|
|
m_localOBB.sphereRadius = 0.0f;
|
|
SetNeverCullMode();
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Otherwise, find the tightest bounds from our calculated centroid
|
|
//-----------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
centroid /= static_cast<Stuff::Scalar>(bounded_children);
|
|
m_localOBB.localToParent.BuildTranslation(centroid);
|
|
m_localOBB.sphereRadius = -1.0f;
|
|
for (i=0; i<switch_count; ++i)
|
|
{
|
|
Element *element = m_list[i];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
if (element->GetCullMode() == VolumeCullMode)
|
|
{
|
|
Stuff::LinearMatrix4D child_to_parent = element->GetLocalToParent();
|
|
Stuff::Point3D child_center(element->m_localOBB.localToParent);
|
|
Stuff::Point3D child_center_in_parent;
|
|
child_center_in_parent.Multiply(child_center, child_to_parent);
|
|
child_center_in_parent -= centroid;
|
|
Stuff::Scalar range =
|
|
child_center_in_parent.GetLength()
|
|
+ element->m_localOBB.sphereRadius;
|
|
if (range > m_localOBB.sphereRadius)
|
|
m_localOBB.sphereRadius = range;
|
|
}
|
|
}
|
|
}
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
}
|
|
BoundsAreRight();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::FullSphereSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
Verify(
|
|
IsMatrixDirty() && AreBoundsWrong()
|
|
&& GetCullMode()==VolumeCullMode && IsBoundedBySphere()
|
|
);
|
|
SYNC_LOGIC("Sphere::Full::Switch");
|
|
|
|
//
|
|
//--------------------
|
|
// 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();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
WORD i;
|
|
Verify(m_list.GetLength() < 65536);
|
|
WORD switch_count = static_cast<WORD>(m_list.GetLength());
|
|
Stuff::Point3D centroid = Stuff::Point3D::Identity;
|
|
WORD bounded_children = 0;
|
|
for (i=0; i<switch_count; ++i)
|
|
{
|
|
Element *element = m_list[i];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
if (element->GetCullMode() == VolumeCullMode)
|
|
{
|
|
++bounded_children;
|
|
Stuff::LinearMatrix4D child_to_parent = element->GetLocalToParent();
|
|
Stuff::Point3D child_center(element->m_localOBB.localToParent);
|
|
Stuff::Point3D child_center_in_parent;
|
|
child_center_in_parent.Multiply(child_center, child_to_parent);
|
|
centroid += child_center_in_parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// If we have no bounded children, we will never cull
|
|
//---------------------------------------------------
|
|
//
|
|
if (!bounded_children)
|
|
{
|
|
m_localOBB.sphereRadius = 0.0f;
|
|
SetNeverCullMode();
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// Otherwise, find the tightest bounds from our calculated centroid
|
|
//-----------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
centroid /= static_cast<Stuff::Scalar>(bounded_children);
|
|
m_localOBB.localToParent.BuildTranslation(centroid);
|
|
m_localOBB.sphereRadius = -1.0f;
|
|
for (i=0; i<switch_count; ++i)
|
|
{
|
|
Element *element = m_list[i];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
if (element->GetCullMode() == VolumeCullMode)
|
|
{
|
|
Stuff::LinearMatrix4D child_to_parent = element->GetLocalToParent();
|
|
Stuff::Point3D child_center(element->m_localOBB.localToParent);
|
|
Stuff::Point3D child_center_in_parent;
|
|
child_center_in_parent.Multiply(child_center, child_to_parent);
|
|
child_center_in_parent -= centroid;
|
|
Stuff::Scalar range =
|
|
child_center_in_parent.GetLength()
|
|
+ element->m_localOBB.sphereRadius;
|
|
if (range > m_localOBB.sphereRadius)
|
|
m_localOBB.sphereRadius = range;
|
|
}
|
|
}
|
|
}
|
|
Verify(m_localOBB.sphereRadius > 0.0f);
|
|
m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld);
|
|
}
|
|
m_state &= ~(MatrixDirtyFlag|BoundsWrongFlag);
|
|
SetSyncState();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::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::Switch");
|
|
|
|
//
|
|
//-------------------------------
|
|
// 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();
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::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::Switch");
|
|
|
|
//
|
|
//--------------------
|
|
// 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();
|
|
|
|
//
|
|
//------------------------------
|
|
// Transform the bounding sphere
|
|
//------------------------------
|
|
//
|
|
m_worldOBB.Multiply(m_localOBB, m_localToWorld);
|
|
MatrixIsClean();
|
|
|
|
//
|
|
//--------------------
|
|
// Update our children
|
|
//--------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->Sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::BoundsWrongOBBSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
STOP(("OBB's cannot recalculate bounds!"));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::FullOBBSyncMethod()
|
|
{
|
|
Check_Object(this);
|
|
STOP(("OBB's cannot recalculate bounds!"));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool
|
|
ElementRenderer::SwitchElement::CastCulledRay(CollisionQuery *query)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(query);
|
|
Verify(!query->m_data);
|
|
|
|
//
|
|
//-----------------------------
|
|
// Test it against the MLRShape
|
|
//-----------------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
if (element->CastRay(query))
|
|
{
|
|
if (!query->m_data)
|
|
query->m_data = m_data;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
if (element->CastRay(query))
|
|
{
|
|
if (!query->m_data)
|
|
query->m_data = m_data;
|
|
return true;
|
|
}
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
if (element->CastRay(query))
|
|
{
|
|
if (!query->m_data)
|
|
query->m_data = m_data;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ElementRenderer::Element*
|
|
ElementRenderer::SwitchElement::FindSmallestElementContainingCulled(SphereTest *test)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(test);
|
|
|
|
//
|
|
//------------------
|
|
// Draw our children
|
|
//------------------
|
|
//
|
|
Element *result = this;
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
Element *temp = element->FindSmallestElementContaining(test);
|
|
if (temp)
|
|
result = temp;
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
Element *temp = element->FindSmallestElementContaining(test);
|
|
if (temp)
|
|
result = temp;
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
Element *temp = element->FindSmallestElementContaining(test);
|
|
if (temp)
|
|
result = temp;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::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
|
|
|
|
//
|
|
//------------------------------
|
|
// Only draw the switch selected
|
|
//------------------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, inherited_state);
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, inherited_state);
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, inherited_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::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
|
|
|
|
//
|
|
//------------------------------
|
|
// Only draw the switch selected
|
|
//------------------------------
|
|
//
|
|
if (m_switch < m_list.GetLength())
|
|
{
|
|
Element *element = m_list[m_switch];
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, &mixed);
|
|
}
|
|
}
|
|
else if (m_switch & 0xf0) // special case
|
|
{
|
|
Element *element = GetFirstSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, &mixed);
|
|
}
|
|
element = GetSecondSwitchElement();
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
camera->DrawElement(element, &mixed);
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::CleanDamage()
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::ApplyDamage(
|
|
const Stuff::LinearMatrix4D &damage_spot,
|
|
Stuff::Scalar radius
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::ApplyDamageDecal(
|
|
const Stuff::LinearMatrix4D &damage_spot,
|
|
Stuff::Scalar radius,
|
|
MidLevelRenderer::MLRTexture *texture
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(texture);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::CastShadow(
|
|
const Stuff::LinearMatrix4D &shadow_to_world,
|
|
const Stuff::UnitVector3D &sun_in_world,
|
|
Stuff::Scalar radius
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
if (!IsShadowed())
|
|
return;
|
|
WORD current = GetSwitch();
|
|
if (current < GetActiveCount())
|
|
{
|
|
Element *element = GetIndexedElement(current);
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->CastShadow(shadow_to_world, sun_in_world, radius);
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::SwitchElement::MakeFootStep(
|
|
MidLevelRenderer::MLRShape *shape,
|
|
const Stuff::LinearMatrix4D &foot,
|
|
Stuff::Scalar radius,
|
|
MidLevelRenderer::MLRTexture *tex
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
|
|
WORD current = GetSwitch();
|
|
if (current < GetActiveCount())
|
|
{
|
|
Element *element = GetIndexedElement(current);
|
|
if (element)
|
|
{
|
|
Check_Object(element);
|
|
element->MakeFootStep(shape, foot, radius, tex);
|
|
}
|
|
}
|
|
}
|
|
|