#include "ElementRendererHeaders.hpp" #include //#define BOUNDS_BUG //############################################################################ //############################ ShapeLODElement ################################ //############################################################################ HGOSHEAP ElementRenderer::ShapeLODElement::s_Heap = NULL; ElementRenderer::ShapeLODElement::ClassData* ElementRenderer::ShapeLODElement::DefaultData = NULL; ElementRenderer::Element::SyncMethod ElementRenderer::ShapeLODElement::SyncMethods[SyncStateCount]= { // // Root mode // SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), // // Sphere Cull // SYNC_METHOD(ShapeLODElement, CleanSphere), SYNC_METHOD(ShapeLODElement, DirtySphere), SYNC_METHOD(ShapeLODElement, CleanSphere), SYNC_METHOD(ShapeLODElement, DirtySphere), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), // // OBB Cull // SYNC_METHOD(ShapeLODElement, CleanOBB), SYNC_METHOD(ShapeLODElement, DirtyOBB), SYNC_METHOD(ShapeLODElement, CleanOBB), SYNC_METHOD(ShapeLODElement, DirtyOBB), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), // // Never Cull // SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(ShapeLODElement, Clean), SYNC_METHOD(ShapeLODElement, Dirty), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), SYNC_METHOD(Element, None), SYNC_METHOD(Element, Defer), // // Always Cull // SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None), SYNC_METHOD(Element, None) }; ElementRenderer::Element::DrawMethod ElementRenderer::ShapeLODElement::DrawMethods[DrawStateCount]= { // // No billboarding // DRAW_METHOD(ShapeLODElement, Inherit), DRAW_METHOD(ShapeLODElement, Override) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::InitializeClass() { Verify(!s_Heap); s_Heap = gos_CreateMemoryHeap("ShapeLOD", 0, g_LibraryHeap); Check_Pointer(s_Heap); Verify(!DefaultData); DefaultData = new ClassData( ShapeLODElementClassID, "ElementRenderer::ShapeLODElement", BaseClass::DefaultData, reinterpret_cast(&Make) ); Check_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::TerminateClass() { Check_Object(DefaultData); delete DefaultData; DefaultData = NULL; Check_Pointer(s_Heap); gos_DestroyMemoryHeap(s_Heap); s_Heap = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::ShapeLODElement::ShapeLODElement( ClassData *class_data, Stuff::MemoryStream *stream, int version, ShapeHolder shapes ): Element(class_data, stream, version) { Check_Pointer(this); Check_Object(stream); if (version<10) STOP(("Application must be recompiled to use this data!")); unsigned index = (m_state>>SyncStateBit) & SyncStateMask; Verify(index < SyncStateCount); m_sync = SyncMethods[index]; index = (m_state>>CullStateBit) & CullStateMask; Verify(index < CullStateCount); m_cameraCull = CullMethods[index].m_cameraCull; m_rayCull = CullMethods[index].m_rayCull; m_sphereTest = CullMethods[index].m_sphereTest; index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; // //---------------------- // Read in the LOD count //---------------------- // m_worldToLocal = Stuff::LinearMatrix4D::Identity; WORD count; *stream >> count; m_ranges.SetLength(count); m_MLRShapes.SetLength(count); int mlr_version = MidLevelRenderer::ReadMLRVersion(stream); // //----------------------------------------------- // For older versions, we create all the LOD info //----------------------------------------------- // if (version < 13) { for (WORD i=0; i> class_id; switch(class_id) { case MidLevelRenderer::MLRShapeClassID: m_MLRShapes[i] = MidLevelRenderer::MLRShape::Make(stream, mlr_version); break; case MidLevelRenderer::MLRCulturShapeClassID: m_MLRShapes[i] = MidLevelRenderer::MLRCulturShape::Make(stream, mlr_version); break; } Check_Object(m_MLRShapes[i]); *stream >> m_ranges[i].m_nearSquared >> m_ranges[i].m_farSquared; } } // //------------------------------------------------------------------------------------ // Newer versions of the data will allow us to skip reading LODs that won't be visible //------------------------------------------------------------------------------------ // else { WORD used=0; for (WORD i=0; i> m_ranges[used].m_nearSquared >> m_ranges[used].m_farSquared; // //----------------------------------------------------- // If this can't be seen because of the offset, dump it //----------------------------------------------------- // int length; *stream >> length; if (i m_ranges[used].m_farSquared) { stream->AdvancePointer(length); continue; } // //------------------------------------------------------------------------------------ // If this is the first visible LOD after we have dropped one, reset the near clipping // plane to zero //------------------------------------------------------------------------------------ // else if (!used && i>0) m_ranges[used].m_nearSquared = 0.0f; // //------------------------------------------------------------------- // If we were given a shape holder, have it make the MLR shape for us //------------------------------------------------------------------- // if (shapes) m_MLRShapes[used] = (*shapes)(stream, mlr_version, length); // //--------------------------------------------------------------- // Otherwise, read in the class ID and call the right constructor //--------------------------------------------------------------- // else { RegisteredClass::ClassID class_id; *stream >> class_id; switch(class_id) { case MidLevelRenderer::MLRShapeClassID: m_MLRShapes[used] = MidLevelRenderer::MLRShape::Make(stream, mlr_version); break; case MidLevelRenderer::MLRCulturShapeClassID: m_MLRShapes[used] = MidLevelRenderer::MLRCulturShape::Make(stream, mlr_version); break; } } Check_Object(m_MLRShapes[used]); ++used; } Verify(used>0); if (used < count) { m_ranges.SetLength(used); m_MLRShapes.SetLength(used); } } // //------------------------------------------------ // Bump the OBB a tad to help with clipping errors //------------------------------------------------ // if (IsBoundedBySphere()) m_localOBB.sphereRadius *= 1.01f; else { m_localOBB.axisExtents *= 1.01f; m_localOBB.sphereRadius = m_localOBB.axisExtents.GetLength(); } if (ShapeElement::s_CheckBounds && !CheckBounds()) ShapeElement::s_BoundsWereBad = true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::ShapeLODElement::ShapeLODElement(ClassData *class_data): Element(class_data) { Check_Pointer(this); unsigned index = (m_state>>SyncStateBit) & SyncStateMask; Verify(index < SyncStateCount); m_sync = SyncMethods[index]; index = (m_state>>CullStateBit) & CullStateMask; Verify(index < CullStateCount); m_cameraCull = CullMethods[index].m_cameraCull; m_rayCull = CullMethods[index].m_rayCull; m_sphereTest = CullMethods[index].m_sphereTest; index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; m_worldToLocal = Stuff::LinearMatrix4D::Identity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::ShapeLODElement::~ShapeLODElement() { Check_Object(this); for (WORD i=0; iDetachReference(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::ShapeLODElement* ElementRenderer::ShapeLODElement::Make( Stuff::MemoryStream *stream, int version, ShapeHolder shapes ) { Check_Object(stream); gos_PushCurrentHeap(s_Heap); ShapeLODElement *element = new ShapeLODElement(DefaultData, stream, version, shapes); gos_PopCurrentHeap(); return element; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::Save(Stuff::MemoryStream *stream) { Check_Object(this); Check_Object(stream); BaseClass::Save(stream); // //----------------------------- // Save the shape to the stream //----------------------------- // Verify(m_ranges.GetLength() < 65536); *stream << static_cast(m_ranges.GetLength()); MidLevelRenderer::WriteMLRVersion(stream); for (WORD i=0; iGetIndex(); m_MLRShapes[i]->Save(stream); unsigned length = stream->GetIndex() - index; stream->RewindPointer(length + sizeof(int)); *stream << length; stream->AdvancePointer(length); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool ElementRenderer::ShapeLODElement::CheckBounds() { Check_Object(this); int meshes = m_MLRShapes.GetLength(); for(int m=0; mDetachReference(); } m_MLRShapes[index] = shape; m_MLRShapes[index]->AttachReference(); m_ranges[index] = ranges; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This is necessary for attaching LOD's into the list that might miss certain // meshes in the middle ie.. 4/5 of the LOD's have a toe, but lod 2 doesn't // Needed for the exporter. The grouping there doesn't allow counters. int ElementRenderer::ShapeLODElement::AttachLODFirstAvailableSlot( MidLevelRenderer::MLRShape *shape, const LODElement::Entry &ranges ) { Check_Object(this); Check_Object(shape); for (unsigned short index = 0; index < m_MLRShapes.GetLength(); index ++) { if (m_MLRShapes[index] == NULL) { m_MLRShapes[index] = shape; m_MLRShapes[index]->AttachReference(); m_ranges[index] = ranges; return index; } } STOP(("ShapeLODElement: Can't attach another LOD, already full!")); return -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::GetLOD( WORD index, MidLevelRenderer::MLRShape **shape, const LODElement::Entry **ranges ) { Check_Object(this); Check_Object(m_MLRShapes[index]); *shape = m_MLRShapes[index]; (*shape)->AttachReference(); *ranges = &m_ranges[index]; } void ElementRenderer::ShapeLODElement::SetLOD( WORD index, const LODElement::Entry &ranges ) { Check_Object(this); m_ranges[index]=ranges; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::SetSize(WORD max_size) { Check_Object(this); DWORD i = m_MLRShapes.GetLength(); m_MLRShapes.SetLength(max_size); m_ranges.SetLength(max_size); for (;i(m_MLRShapes.GetLength()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::SetSyncState() { Check_Object(this); Verify( GetCullMode() != VolumeCullMode || AreBoundsWrong() || IsSyncDeferred() || ( m_localOBB.sphereRadius>0.0f && (IsMatrixDirty() || m_worldOBB.sphereRadius>0.0f ) ) ); Verify( GetCullMode() == RootMode || m_parent != NULL || AreBoundsWrong() ); unsigned index = (m_state>>SyncStateBit) & SyncStateMask; Verify(index < SyncStateCount); m_sync = SyncMethods[index]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::SetDrawState() { Check_Object(this); unsigned index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::DirtySyncMethod() { Check_Object(this); Verify(IsMatrixDirty() && GetCullMode()==NeverCullMode); SYNC_LOGIC("Dirty::ShapeLOD"); // //-------------------- // Update our matrices //-------------------- // m_localToParent = m_newLocalToParent; Check_Object(m_parent); if (!IsLocalToParentIdentity()) m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); m_worldToLocal.Invert(m_localToWorld); MatrixIsClean(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::CleanSphereSyncMethod() { Check_Object(this); Verify(m_localOBB.sphereRadius > 0.0f); Verify( !IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere() ); SYNC_LOGIC("Sphere::Clean::ShapeLOD"); // //------------------------------- // Update our m_localToWorld matrix //------------------------------- // Check_Object(m_parent); if (!IsLocalToParentIdentity()) m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); m_worldToLocal.Invert(m_localToWorld); // //------------------------------ // Transform the bounding sphere //------------------------------ // m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::DirtySphereSyncMethod() { Check_Object(this); Verify(m_localOBB.sphereRadius > 0.0f); Verify( IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere() ); SYNC_LOGIC("Sphere::Dirty::ShapeLOD"); // //-------------------- // Update our matrices //-------------------- // m_localToParent = m_newLocalToParent; Check_Object(m_parent); if (!IsLocalToParentIdentity()) m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); m_worldToLocal.Invert(m_localToWorld); // //------------------------------ // Transform the bounding sphere //------------------------------ // m_worldOBB.MultiplySphereOnly(m_localOBB, m_localToWorld); MatrixIsClean(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::CleanOBBSyncMethod() { Check_Object(this); Verify( !IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB() ); Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f)); SYNC_LOGIC("OBB::Clean::ShapeLOD"); // //------------------------------- // Update our m_localToWorld matrix //------------------------------- // Check_Object(m_parent); if (!IsLocalToParentIdentity()) m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); m_worldToLocal.Invert(m_localToWorld); // //------------------------------ // Transform the bounding sphere //------------------------------ // m_worldOBB.Multiply(m_localOBB, m_localToWorld); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::DirtyOBBSyncMethod() { Check_Object(this); Verify( IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB() ); Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f)); SYNC_LOGIC("OBB::Dirty::ShapeLOD"); // //-------------------- // Update our matrices //-------------------- // m_localToParent = m_newLocalToParent; Check_Object(m_parent); if (!IsLocalToParentIdentity()) m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld()); else m_localToWorld = m_parent->GetLocalToWorld(); m_worldToLocal.Invert(m_localToWorld); // //------------------------------ // Transform the bounding sphere //------------------------------ // m_worldOBB.Multiply(m_localOBB, m_localToWorld); MatrixIsClean(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool ElementRenderer::ShapeLODElement::CastCulledRay(CollisionQuery *query) { Check_Object(this); Check_Object(query); Verify(!query->m_data); // //----------------------------------- // Figure out the local m_line equation //----------------------------------- // const Stuff::LinearMatrix4D &world_to_local = GetWorldToLocal(); Check_Object(&world_to_local); Stuff::Line3D local_line; Check_Object(query->m_line); local_line.m_origin.Multiply(query->m_line->m_origin, world_to_local); local_line.m_direction.Multiply(query->m_line->m_direction, world_to_local); local_line.m_length = query->m_line->m_length; // //----------------------------- // Test it against the MidLevelRenderer::MLRShape //----------------------------- // Stuff::Normal3D local_normal; if (m_MLRShapes.GetLength()>0) { Check_Object(m_MLRShapes[0]); if (m_MLRShapes[0]->CastRay(&local_line, &local_normal)) { query->m_line->m_length = local_line.m_length; const Stuff::LinearMatrix4D &local_to_world = GetLocalToWorld(); Check_Object(&local_to_world); Check_Pointer(query->m_normal); query->m_normal->Multiply(local_normal, local_to_world); Verify(*query->m_normal * query->m_line->m_direction <= -Stuff::SMALL); query->m_data = m_data; return true; } } return false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::Element* ElementRenderer::ShapeLODElement::FindSmallestElementContainingCulled(SphereTest *test) { Check_Object(this); Check_Object(test); return this; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::InheritDrawMethod( CameraElement *camera, const StateChange *inherited_state, int culling_state ) { Check_Object(this); Check_Object(inherited_state); Check_Object(camera); Verify(culling_state != -1); #if defined(_ARMOR) if (!culling_state) Verify(!Cull(camera)); #endif // //-------------------------------------- // Draw our bounds if we are supposed to //-------------------------------------- // #if defined(LAB_ONLY) Callback *callback = GetCallbackSet(); unsigned index = GetCallbackIndex(); if (callback && callback[index]) { Check_Pointer(callback[index]); (*callback[index])(camera, inherited_state, culling_state, this); } else if (ShapeElement::s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state) { Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f); if (IsBoundedBySphere()) DrawSphere(m_worldOBB, color, camera, inherited_state, culling_state); else DrawOBB(m_worldOBB, color, camera, inherited_state, culling_state); } #endif // //---------------------------------------------------------------------- // We need to find out which lod the camera should traverse into, so // transform the camera into LOD space and get the distance from the LOD //---------------------------------------------------------------------- // Stuff::Point3D camera_origin(camera->GetLocalToWorld()); Stuff::Point3D lod_origin(GetLocalToWorld()); camera_origin -= lod_origin; Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset; Min_Clamp(distance, 0.0f); // //--------------------------------------------------- // Search the lod entries looking for the first match //--------------------------------------------------- // for (WORD i=0; im_nearSquared <= distance && entry->m_farSquared >= distance) { // //---------------------------- // Set up the draw information //---------------------------- // MidLevelRenderer::DrawShapeInformation info; Check_Object(m_MLRShapes[i]); info.shape = m_MLRShapes[i]; info.clippingFlags.SetClippingState(culling_state); Check_Object(&m_localToWorld); info.shapeToWorld = &m_localToWorld; Check_Object(&m_worldToLocal); info.worldToShape = &m_worldToLocal; info.state = inherited_state->GetMLRState(); #if defined(LAB_ONLY) if (LODElement::s_ShadeLODs && im_lights); info.activeLights = (info.nrOfActiveLights>0) ? &culled_lights[0] : NULL; // //--------------- // Draw the shape //--------------- // MidLevelRenderer::MLRClipper *clipper = camera->GetClipper(); Check_Object(clipper); Stop_Timer(Graph_Traversal); ELEMENT_RENDER("Culling::ShapeLOD::Inherited"); Start_Timer(Draw_Shapes); clipper->DrawShape(&info); Stop_Timer(Draw_Shapes); Start_Timer(Graph_Traversal); break; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::OverrideDrawMethod( CameraElement *camera, const StateChange *inherited_state, int culling_state ) { Check_Object(this); Check_Object(inherited_state); Check_Object(camera); Verify(culling_state != -1); #if defined(_ARMOR) if (!culling_state) Verify(!Cull(camera)); #endif // //------------------- // Mix the properties //------------------- // Check_Object(m_stateChange); StateChange mixed; mixed.Mix(*inherited_state, *m_stateChange); MixLights( mixed.m_lights, inherited_state->m_lights, m_stateChange->m_lights ); // //-------------------------------------- // Draw our bounds if we are supposed to //-------------------------------------- // #if defined(LAB_ONLY) Callback *callback = GetCallbackSet(); unsigned index = GetCallbackIndex(); if (callback && callback[index]) { Check_Pointer(callback[index]); (*callback[index])(camera, &mixed, culling_state, this); } else if (ShapeElement::s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state) { Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f); if (IsBoundedBySphere()) DrawSphere(m_worldOBB, color, camera, &mixed, culling_state); else DrawOBB(m_worldOBB, color, camera, &mixed, culling_state); } #endif // //---------------------------------------------------------------------- // We need to find out which lod the camera should traverse into, so // transform the camera into LOD space and get the distance from the LOD //---------------------------------------------------------------------- // Stuff::Point3D camera_origin(camera->GetLocalToWorld()); Stuff::Point3D lod_origin(GetLocalToWorld()); camera_origin -= lod_origin; Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset; Min_Clamp(distance, 0.0f); // //--------------------------------------------------- // Search the lod entries looking for the first match //--------------------------------------------------- // for (WORD i=0; im_nearSquared <= distance && entry->m_farSquared >= distance) { // //---------------------------- // Set up the draw information //---------------------------- // MidLevelRenderer::DrawShapeInformation info; Check_Object(m_MLRShapes[i]); info.shape = m_MLRShapes[i]; info.clippingFlags.SetClippingState(culling_state); Check_Object(&m_localToWorld); info.shapeToWorld = &m_localToWorld; Check_Object(&m_worldToLocal); info.worldToShape = &m_worldToLocal; info.state = mixed.GetMLRState(); #if defined(LAB_ONLY) if (LODElement::s_ShadeLODs && i0) ? &culled_lights[0] : NULL; // //--------------- // Draw the shape //--------------- // MidLevelRenderer::MLRClipper *clipper = camera->GetClipper(); Check_Object(clipper); Stop_Timer(Graph_Traversal); ELEMENT_RENDER("Culling::ShapeLOD::Overridden"); Start_Timer(Draw_Shapes); clipper->DrawShape(&info); Stop_Timer(Draw_Shapes); Start_Timer(Graph_Traversal); break; } } } extern DWORD Disable_Lights; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int ElementRenderer::ShapeLODElement::CullLights( LightList culled_lights, const LightList lights ) { Check_Object(this); // //---------------------------------------------------------------- // Set up the destination light array and start copying the lights //---------------------------------------------------------------- // if (Disable_Lights) return 0; ELEMENT_RENDER("Culling::ShapeLOD::Lights"); int new_count = 0; Stuff::Point3D center(m_worldOBB.localToParent); for (int i=0; iGetIntensity() < Stuff::SMALL) continue; // //-------------------------------------------------------------------- // If we are copying a light with a range, check it against the bounds // of this node and only copy if it would hit the bounds //-------------------------------------------------------------------- // if (light->IsDerivedFrom(MidLevelRenderer::MLRInfiniteLightWithFalloff::DefaultData)) { MidLevelRenderer::MLRInfiniteLightWithFalloff *bounded_light = Cast_Object(MidLevelRenderer::MLRInfiniteLightWithFalloff*, light); Stuff::Point3D lc(bounded_light->GetLightToWorldMatrix()); Stuff::Vector3D diff; diff.Subtract(center, lc); Stuff::Scalar range = m_worldOBB.sphereRadius + bounded_light->GetFalloffFar(); if (diff.GetLengthSquared() < range*range) culled_lights[new_count++] = light; } // //--------------------------------- // Otherwise it is automatically in //--------------------------------- // else culled_lights[new_count++] = light; if (new_count >= ShapeElement::s_MaxLightsPerShape) break; } Verify(new_count <= MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive); return new_count; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int ElementRenderer::ShapeLODElement::CountTriangles() { Check_Object(this); if (m_MLRShapes.GetLength() > 0) { Check_Object(m_MLRShapes[0]); return m_MLRShapes[0]->GetNumPrimitives(); } return 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::CleanDamage() { Check_Object(this); for (WORD i=0; iGetReferenceCount() == 1); m_MLRShapes[i]->CleanMe(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::ApplyDamage( const Stuff::LinearMatrix4D &damage_spot, Stuff::Scalar radius ) { Check_Object(this); Check_Object(&damage_spot); for (WORD i=0; iGetReferenceCount() == 1); m_MLRShapes[i]->HurtMe(damage_spot, radius); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::ApplyDamageDecal( const Stuff::LinearMatrix4D &damage_spot, Stuff::Scalar radius, MidLevelRenderer::MLRTexture *texture ) { Check_Object(this); Check_Object(&damage_spot); for (WORD i=0; iGetReferenceCount() == 1); m_MLRShapes[i]->HurtMe(damage_spot, radius, texture); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::CastShadow( const Stuff::LinearMatrix4D &shadow_to_world, const Stuff::UnitVector3D &sun_in_world, Stuff::Scalar radius ) { Check_Object(this); if (!IsShadowed()) return; MidLevelRenderer::MLRShape *mlr_shape; const ElementRenderer::LODElement::Entry *entry; Verify(GetSize()>0); GetLOD(static_cast(GetSize()-1), &mlr_shape, &entry); Stuff::LinearMatrix4D world_to_object; world_to_object.Invert(GetLocalToWorld()); Stuff::UnitVector3D sun_in_object; sun_in_object.Multiply(sun_in_world, world_to_object); Stuff::LinearMatrix4D shadow_to_object; shadow_to_object.Multiply(shadow_to_world, world_to_object); mlr_shape->DrawMyShadow(shadow_to_object, sun_in_object, radius); mlr_shape->DetachReference(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::ShapeLODElement::MakeFootStep( MidLevelRenderer::MLRShape *shape, const Stuff::LinearMatrix4D &foot, Stuff::Scalar radius, MidLevelRenderer::MLRTexture *tex ) { Check_Object(this); MidLevelRenderer::MLRShape *mlr_shape; const ElementRenderer::LODElement::Entry *entry; Verify(GetSize()>0); GetLOD(static_cast(GetSize()-1), &mlr_shape, &entry); Stuff::LinearMatrix4D world_to_object; world_to_object.Invert(GetLocalToWorld()); Stuff::LinearMatrix4D foot_to_object; foot_to_object.Multiply(foot, world_to_object); mlr_shape->StepOnMe(shape, foot_to_object, radius, tex); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MidLevelRenderer::MLRShape* ElementRenderer::ShapeLODElement::GetMLRShape( ElementRenderer::CameraElement* cameraElement ) { Check_Object(this); unsigned short lod = 0; if (cameraElement) { Stuff::Point3D camera_origin(cameraElement->GetLocalToWorld()); Stuff::Point3D lod_origin(GetLocalToWorld()); camera_origin -= lod_origin; Stuff::Scalar distance = camera_origin.GetLengthSquared() + LODElement::s_Offset; Min_Clamp(distance, 0.0f); // //--------------------------------------------------- // Search the lod entries looking for the first match //--------------------------------------------------- // for (WORD i=0; im_nearSquared <= distance && entry->m_farSquared >= distance) { lod = i; break; } } } MidLevelRenderer::MLRShape *mlr_shape; const ElementRenderer::LODElement::Entry* ranges; Verify(lod < GetSize()); GetLOD(lod, &mlr_shape, &ranges); mlr_shape->DetachReference(); return mlr_shape; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Returns array of vertex coordinates and number of vertices void ElementRenderer::ShapeLODElement::GetCoordData(Stuff::DynamicArrayOf *array) { Check_Object(this); // //----------------------------------- // First count the number of vertices //----------------------------------- // int total=0; int meshes = m_MLRShapes.GetLength(); int m; for(m=0; mGetNum(); for (int n =0;nFind(n); Check_Object(mesh); const Stuff::Point3D *verts; int count; mesh->GetCoordData(&verts, &count); total += count; } } // //------------------------ // Now allocate the memory //------------------------ // array->SetLength(total); total = 0; for(m=0; mGetNum(); for (int n =0;nFind(n); Check_Object(mesh); const Stuff::Point3D *verts; int count; mesh->GetCoordData(&verts, &count); for (int v=0; v