Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

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;
}
}
}