#include "ElementRendererHeaders.hpp" #include "MultiLODElement.hpp" //#define SPEW_CULL_INFO "your_name_here" //############################################################################ //############################ MultiLODElement ################################ //############################################################################ HGOSHEAP ElementRenderer::MultiLODElement::s_Heap = NULL; ElementRenderer::MultiLODElement::ClassData* ElementRenderer::MultiLODElement::DefaultData = NULL; ElementRenderer::Element::DrawMethod ElementRenderer::MultiLODElement::DrawMethods[DrawStateCount]= { DRAW_METHOD(MultiLODElement, Inherit), DRAW_METHOD(MultiLODElement, Override) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::InitializeClass() { Verify(!DefaultData); Verify(!s_Heap); s_Heap = gos_CreateMemoryHeap("MultiLOD", 0, g_LibraryHeap); Check_Pointer(s_Heap); DefaultData = new ClassData( MultiLODElementClassID, "ElementRenderer::MultiLODElement", BaseClass::DefaultData, reinterpret_cast(&Make) ); Check_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::TerminateClass() { Check_Object(DefaultData); delete DefaultData; DefaultData = NULL; Check_Pointer(s_Heap); gos_DestroyMemoryHeap(s_Heap); s_Heap = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::MultiLODElement::MultiLODElement( Stuff::MemoryStream *stream, int version, ShapeHolder shapes ): ListElement(DefaultData, stream, version, shapes, false) { Check_Pointer(this); Check_Object(stream); unsigned index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; // //------------------------------------------------------ // Older Multi-LOD versions just read in the whole thing //------------------------------------------------------ // if (version < 13) { if (version >= 11) *stream >> m_activeChildren; else { int count; *stream >> count; Verify(static_cast(count) < 65536); m_activeChildren = static_cast(count); } m_list.SetLength(m_activeChildren); for (WORD s=0; sGetCullMode() != AlwaysCullMode) { if (child->m_localOBB.sphereRadius > 0.0f || child->AreBoundsWrong()) child->SetVolumeCullMode(); else child->SetNeverCullMode(); } } // //------------------------- // Load in the LOD settings //------------------------- // Verify(m_list.GetLength() < 65536); WORD lod_count = static_cast(m_list.GetLength()); m_LODs.SetLength(lod_count); for (WORD lod=0; lod> m_LODs[lod].m_nearSquared >> m_LODs[lod].m_fadeInSquared; *stream >> m_LODs[lod].m_fadeOutSquared >> m_LODs[lod].m_farSquared; } } // //------------------------------------------------------------------------ // Newer versions only load in the parts of the Multi-LOD that are visible //------------------------------------------------------------------------ // else { *stream >> m_activeChildren; m_list.SetLength(m_activeChildren); m_LODs.SetLength(m_activeChildren); WORD used=0; for (WORD i=0; i> m_LODs[used].m_nearSquared >> m_LODs[used].m_fadeInSquared; *stream >> m_LODs[used].m_fadeOutSquared >> m_LODs[used].m_farSquared; // //----------------------------------------------------- // If this can't be seen because of the offset, dump it //----------------------------------------------------- // int length; *stream >> length; if (i m_LODs[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_LODs[used].m_nearSquared = 0.0f; m_LODs[used].m_fadeInSquared = 0.0f; } // //----------------------------------- // Now go ahead and read in the child //----------------------------------- // m_list[used] = Create(stream, version); Element *child = m_list[used]; SetParentElement(child, this); if (child->GetCullMode() != AlwaysCullMode) { if (child->m_localOBB.sphereRadius > 0.0f || child->AreBoundsWrong()) child->SetVolumeCullMode(); else child->SetNeverCullMode(); } ++used; } Verify(used>0); if (used < m_activeChildren) { m_list.SetLength(used); m_LODs.SetLength(used); m_activeChildren = used; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::MultiLODElement::MultiLODElement(): ListElement(DefaultData) { Check_Pointer(this); unsigned index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::MultiLODElement::~MultiLODElement() { Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ElementRenderer::MultiLODElement* ElementRenderer::MultiLODElement::Make( Stuff::MemoryStream *stream, int version, ShapeHolder shapes ) { Check_Object(stream); gos_PushCurrentHeap(s_Heap); MultiLODElement *element = new MultiLODElement(stream, version, shapes); gos_PopCurrentHeap(); return element; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::Save(Stuff::MemoryStream *stream) { Check_Object(this); Check_Object(stream); // //----------------------------------------- // We don't want ListElement doing anything //----------------------------------------- // BaseClass::BaseClass::Save(stream); // //-------------------- // Save each LOD entry //-------------------- // WORD child_count = (WORD)m_list.GetLength(); Verify((unsigned)m_list.GetLength() < 65536); Verify(m_list.GetLength() == child_count); *stream << child_count; for (WORD i=0; iGetIndex(); m_list[i]->Save(stream); unsigned length = stream->GetIndex() - index; stream->RewindPointer(length + sizeof(int)); *stream << length; stream->AdvancePointer(length); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::AttachLOD( WORD index, Element *element, const Entry &entry ) { Check_Object(this); Verify(index < m_LODs.GetLength()); m_LODs[index] = entry; AttachIndexedChild(index, element); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // 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::MultiLODElement::AttachLODFirstAvailableSlot( Element *element, const Entry &entry ) { Check_Object(this); for (unsigned short index = 0; index < m_LODs.GetLength(); index ++) { // // When initialized these distances are set to 0 (in SetSize) // if (Stuff::Small_Enough(m_LODs[index].m_nearSquared) && Stuff::Small_Enough( m_LODs[index].m_farSquared)) { m_LODs[index] = entry; AttachIndexedChild(index, element); return index; } } STOP(("MultiLODElement: Can't attach another LOD, already full!")); return -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::SetSize(WORD max_size) { Check_Object(this);/* gos_PushCurrentHeap(s_Heap); for (WORD i=0; i(m_LODs.GetLength()); for (WORD i=0; im_nearSquared = 0.0f; entry->m_fadeInSquared = 0.0f; entry->m_fadeOutSquared = 0.0f; entry->m_farSquared = 0.0f; break; } } BaseClass::DetachChild(child); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::SetDrawState() { Check_Object(this); unsigned index = (m_state>>DrawStateBit) & DrawStateMask; Verify(index < DrawStateCount); m_draw = DrawMethods[index]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::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 // //---------------------------------------------------------------------- // 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); // //------------------------------------------------- // Evaluate each LOD to see if it needs to be drawn //------------------------------------------------- // for (int i=m_LODs.GetLength()-1; i>=0; --i) { Entry *entry = &m_LODs[i]; if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance) { Element *element = m_list[i]; Check_Object(element); // //----------------------------------------- // Set the fade color if we are in the band //----------------------------------------- // StateChange mixed(*inherited_state); unsigned j; for (j=0; jm_lights[j]; ++j) { Check_Object(inherited_state->m_lights[j]); mixed.m_lights[j] = inherited_state->m_lights[j]; } if(jm_fadeInSquared) { Stuff::Scalar temp = (distance - entry->m_nearSquared) / (entry->m_fadeInSquared - entry->m_nearSquared); Verify(temp >= 0.0f && temp <= 1.0f); FadeColor.alpha *= temp; mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode); mixed.DisableChildAlphaControl(); mixed.SetRenderPriority(StateChange::AlphaPriority); mixed.DisableChildRenderPriorityControl(); } else if (distance > entry->m_fadeOutSquared) { Stuff::Scalar temp = (entry->m_farSquared - distance) / (entry->m_farSquared - entry->m_fadeOutSquared); Verify(temp >= 0.0f && temp <= 1.0f); FadeColor.alpha *= temp; mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode); mixed.DisableChildAlphaControl(); mixed.SetRenderPriority(StateChange::AlphaPriority); mixed.DisableChildRenderPriorityControl(); } camera->DrawElement(element, &mixed); FadeColor.alpha = fade; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ElementRenderer::MultiLODElement::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 ); // //---------------------------------------------------------------------- // 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); // //------------------------------------------------- // Evaluate each LOD to see if it needs to be drawn //------------------------------------------------- // for (int i=m_LODs.GetLength()-1; i>=0; --i) { Entry *entry = &m_LODs[i]; if (entry->m_nearSquared <= distance && entry->m_farSquared >= distance) { Element *element = m_list[i]; Check_Object(element); // //----------------------------------------- // Set the fade color if we are in the band //----------------------------------------- // Stuff::Scalar fade = FadeColor.alpha; if (distance < entry->m_fadeInSquared) { Stuff::Scalar temp = (distance - entry->m_nearSquared) / (entry->m_fadeInSquared - entry->m_nearSquared); Verify(temp >= 0.0f && temp <= 1.0f); FadeColor.alpha *= temp; mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode); mixed.DisableChildAlphaControl(); mixed.SetRenderPriority(StateChange::AlphaPriority); mixed.DisableChildRenderPriorityControl(); } else if (distance > entry->m_fadeOutSquared) { Stuff::Scalar temp = (entry->m_farSquared - distance) / (entry->m_farSquared - entry->m_fadeOutSquared); Verify(temp >= 0.0f && temp <= 1.0f); FadeColor.alpha *= temp; mixed.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode); mixed.DisableChildAlphaControl(); mixed.SetRenderPriority(StateChange::AlphaPriority); mixed.DisableChildRenderPriorityControl(); } camera->DrawElement(element, &mixed); FadeColor.alpha = fade; } } }