#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(&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 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 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 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 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 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 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 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 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 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; iGetClassName(), 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; iIsDerivedFrom(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; iGetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); // //------------------------------------------------------------------------ // If our bounds are locked, just run the children and transform our local // bounds //------------------------------------------------------------------------ // Stuff::ChainIteratorOf 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; iGetClassData()->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; iGetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); // //------------------------------ // Transform the bounding sphere //------------------------------ // m_worldOBB.Multiply(m_localOBB, m_localToWorld); // //-------------------- // Update our children //-------------------- // Stuff::ChainIteratorOf 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 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 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 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 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 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 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 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 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 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 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 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 *array) { Check_Object(this); Stuff::ChainIteratorOf children(&m_group); Element *child; while ((child = children.ReadAndNext()) != NULL) { Stuff::DynamicArrayOf child_array; child->GetCoordData(&child_array); int point_count = array->GetLength(); array->SetLength(point_count+child_array.GetLength()); for (int v=0; v