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.
577 lines
16 KiB
C++
577 lines
16 KiB
C++
#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<Factory>(&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<unsigned>(count) < 65536);
|
|
m_activeChildren = static_cast<WORD>(count);
|
|
}
|
|
m_list.SetLength(m_activeChildren);
|
|
for (WORD s=0; s<m_activeChildren; ++s)
|
|
{
|
|
m_list[s] = Create(stream, version);
|
|
Element *child = m_list[s];
|
|
SetParentElement(child, this);
|
|
if (child->GetCullMode() != 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<WORD>(m_list.GetLength());
|
|
m_LODs.SetLength(lod_count);
|
|
for (WORD lod=0; lod<lod_count; ++lod)
|
|
{
|
|
*stream >> 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_activeChildren; ++i)
|
|
{
|
|
*stream >> 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_activeChildren-1 && LODElement::s_Offset > 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; i<child_count; ++i)
|
|
{
|
|
Check_Object(m_list[i]);
|
|
*stream << m_LODs[i].m_nearSquared << m_LODs[i].m_fadeInSquared;
|
|
*stream << m_LODs[i].m_fadeOutSquared << m_LODs[i].m_farSquared;
|
|
|
|
//
|
|
//------------------------------------------------------------
|
|
// Make sure to include the length of the child substream here
|
|
//------------------------------------------------------------
|
|
//
|
|
*stream << (int)0;
|
|
unsigned index = stream->GetIndex();
|
|
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<max_size; ++i)
|
|
{
|
|
m_LODs[i].m_nearSquared = 0.0f;
|
|
m_LODs[i].m_fadeInSquared = 0.0f;
|
|
m_LODs[i].m_fadeOutSquared = 0.0f;
|
|
m_LODs[i].m_farSquared = 0.0f;
|
|
}
|
|
*/
|
|
DWORD i = m_LODs.GetLength();
|
|
m_LODs.SetLength(max_size);
|
|
for (;i<max_size; ++i)
|
|
{
|
|
m_LODs[i].m_nearSquared = 0.0f;
|
|
m_LODs[i].m_fadeInSquared = 0.0f;
|
|
m_LODs[i].m_fadeOutSquared = 0.0f;
|
|
m_LODs[i].m_farSquared = 0.0f;
|
|
}
|
|
|
|
BaseClass::SetSize(max_size);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ElementRenderer::MultiLODElement::DetachChild(Element *child)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(child);
|
|
|
|
//
|
|
//-------------------------------
|
|
// Find the element and unhook it
|
|
//-------------------------------
|
|
//
|
|
Verify(m_LODs.GetLength() < 65536);
|
|
WORD lod_count = static_cast<WORD>(m_LODs.GetLength());
|
|
for (WORD i=0; i<lod_count; ++i)
|
|
{
|
|
Entry *entry = &m_LODs[i];
|
|
if (m_list[i] == child)
|
|
{
|
|
entry->m_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; j<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive && inherited_state->m_lights[j]; ++j)
|
|
{
|
|
Check_Object(inherited_state->m_lights[j]);
|
|
mixed.m_lights[j] = inherited_state->m_lights[j];
|
|
}
|
|
if(j<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive)
|
|
{
|
|
mixed.m_lights[j] = NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
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;
|
|
}
|
|
}
|
|
}
|