Files
firestorm/Gameleap/code/mw4/Libraries/Adept/CollisionVolume.cpp
T
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

1261 lines
34 KiB
C++

#include "AdeptHeaders.hpp"
#include "CollisionVolume.hpp"
#include "Entity.hpp"
#include <ElementRenderer\CameraElement.hpp>
#include <ElementRenderer\StateChange.hpp>
#include "Application.hpp"
//#define OBB_BUG "jmalbert"
static ElementRenderer::Element::Callback *Old_Callbacks = NULL;
static ElementRenderer::Element::Callback
Callbacks[1<<ElementRenderer::Element::CallbackBits]=
{
NULL,
CollisionVolume::DrawOBBRejectedCallback,
CollisionVolume::DrawOBBTraversedCallback,
CollisionVolume::DrawOBBStruckCallback,
CollisionVolume::DrawOBBPolygonsTestedCallback,
NULL,
NULL,
NULL
};
static RGBAColor Blue(0.5f, 0.5f, 1.0f, 0.3f);
static RGBAColor Green(0.5f, 1.0f, 0.5f, 0.3f);
static RGBAColor Sparkles[4]=
{
RGBAColor(1.0f, 0.5f, 0.5f, 0.3f),
RGBAColor(1.0f, 1.0f, 0.5f, 0.3f),
RGBAColor(0.5f, 0.5f, 0.5f, 0.3f),
RGBAColor(1.0f, 1.0f, 1.0f, 0.3f)
};
static bool Show_Line_vs_OBBs=false;
static bool Show_OBB_vs_OBBs=false;
static bool __stdcall Check_ShowLineVsOBBs() {return Show_Line_vs_OBBs;}
static bool __stdcall Check_ShowOBBVsOBBs() {return Show_OBB_vs_OBBs;}
static void
__stdcall Activate_ShowLineVsOBBs()
{
Show_Line_vs_OBBs = !Show_Line_vs_OBBs;
if (Show_Line_vs_OBBs)
{
if (Show_OBB_vs_OBBs)
Show_OBB_vs_OBBs = false;
else
{
Old_Callbacks = ElementRenderer::Element::GetCallbackSet();
ElementRenderer::Element::UseCallbackSet(Callbacks);
}
}
else
{
ElementRenderer::Element::UseCallbackSet(Old_Callbacks);
Old_Callbacks = NULL;
}
}
static void
__stdcall Activate_ShowOBBVsOBBs()
{
Show_OBB_vs_OBBs = !Show_OBB_vs_OBBs;
if (Show_OBB_vs_OBBs)
{
if (Show_Line_vs_OBBs)
Show_Line_vs_OBBs = false;
else
{
Old_Callbacks = ElementRenderer::Element::GetCallbackSet();
ElementRenderer::Element::UseCallbackSet(Callbacks);
}
}
else
{
ElementRenderer::Element::UseCallbackSet(Old_Callbacks);
Old_Callbacks = NULL;
}
}
DECLARE_TIMER(static, Poly_Ray_Cast);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::InitializeClass()
{
AddDebuggerMenuItem(
"Libraries\\Adept\\Show Lines vs. OBBs",
Check_ShowLineVsOBBs,
Activate_ShowLineVsOBBs,
NULL
);
AddDebuggerMenuItem(
"Libraries\\Adept\\Show OBB vs. OBBs",
Check_ShowOBBVsOBBs,
Activate_ShowOBBVsOBBs,
NULL
);
Initialize_Timer(Poly_Ray_Cast, "Poly Raycast Time");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::TerminateClass()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume():
Plug(DefaultData),
m_children(NULL)
{
Check_Pointer(this);
//
//--------------------------------
// Read in the bounds and material
//--------------------------------
//
m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
m_worldSpaceBounds.sphereRadius = -1.0f;
m_localSpaceBounds.localToParent = LinearMatrix4D::Identity;
m_localSpaceBounds.sphereRadius = -1.0f;
m_material = GrassMaterial;
m_entity = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume(
Entity *entity,
MemoryStream *stream,
int version
):
Plug(DefaultData),
m_children(NULL)
{
Check_Pointer(this);
Check_Object(entity);
Check_Object(stream);
//
//--------------------------------
// Read in the bounds and material
//--------------------------------
//
*stream >> m_localSpaceBounds;
Verify(m_localSpaceBounds.axisExtents.x >= 0.0f);
Verify(m_localSpaceBounds.axisExtents.y >= 0.0f);
Verify(m_localSpaceBounds.axisExtents.z >= 0.0f);
*stream >> m_material;
m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
m_worldSpaceBounds.sphereRadius = -1.0f;
//
//------------------------------
// Read the name if one is there
//------------------------------
//
if (version > 1)
{
MString joint_name;
*stream >> joint_name;
}
m_entity = entity;
//
//-------------------------------------------------------------------
// If we are reading version 2 or less, remap the material to the new
// settings
//-------------------------------------------------------------------
//
if (version < 3)
{
switch (m_material&0x1f)
{
case 0: //DirtMaterial,
m_material |= BrownDirtMaterial;
break;
case 2: //RockMaterial,
m_material &= ~0x1f;
m_material |= RockMaterial;
break;
case 3: //SteelMaterial,
m_material &= ~0x1f;
m_material |= SteelMaterial;
break;
case 4: //BlacktopMaterial,
m_material &= ~0x1f;
m_material |= BlacktopMaterial;
break;
case 5: //UsMaterial,
m_material &= ~0x1f;
m_material |= UsMaterial;
break;
case 6: //ThemMaterial,
m_material &= ~0x1f;
m_material |= ThemMaterial;
break;
case 7: //VehicleMaterial,
m_material &= ~0x1f;
m_material |= SteelMaterial;
break;
case 8: //GlassMaterial,
m_material &= ~0x1f;
m_material |= GlassMaterial;
break;
case 9: //BrickMaterial,
m_material &= ~0x1f;
m_material |= ConcreteMaterial;
break;
case 10: //GrassMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 11: //WoodMaterial,
m_material &= ~0x1f;
m_material |= WoodMaterial;
break;
case 12: //TreeMaterial,
m_material &= ~0x1f;
m_material |= WoodMaterial;
break;
case 13: //SwampMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 14: //ConcreteMaterial,
m_material &= ~0x1f;
m_material |= ConcreteMaterial;
break;
case 15: //RoughMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 16: //SnowMaterial,
m_material &= ~0x1f;
m_material |= SnowMaterial;
break;
case 17: //UnderbrushMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 18: //ShallowWaterMaterial,
m_material &= ~0x1f;
m_material |= WaterMaterial;
break;
case 19: //MidWaterMaterial,
m_material &= ~0x1f;
m_material |= WaterMaterial;
break;
case 20: //DesertMaterial,
m_material &= ~0x1f;
m_material |= BrownDirtMaterial;
break;
case 21: //FlatSwampMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 22: //ThickSwampMaterial,
m_material &= ~0x1f;
m_material |= GrassMaterial;
break;
case 23: //RedRockMaterial,
m_material &= ~0x1f;
m_material |= RockMaterial;
break;
case 24: //GreyDirtMaterial,
m_material &= ~0x1f;
m_material |= GreyDirtMaterial;
break;
case 25: //DarkDirtMaterial,
m_material &= ~0x1f;
m_material |= DarkBrownDirtMaterial;
break;
case 26: //DarkDesertMaterial,
m_material &= ~0x1f;
m_material |= DarkBrownDirtMaterial;
break;
case 27: //DarkRedRockMaterial,
m_material &= ~0x1f;
m_material |= DarkRockMaterial;
break;
case 28: //DarkRockMaterial,
m_material &= ~0x1f;
m_material |= DarkRockMaterial;
break;
case 29: //DarkConcreteMaterial,
m_material &= ~0x1f;
m_material |= DarkConcreteMaterial;
break;
case 30: //DarkGreyDirtMaterial,
m_material &= ~0x1f;
m_material |= DarkGreyDirtMaterial;
break;
}
}
//
//-----------------------------------------------------------------------
// Non-armatured objects can read in an arbitrary tree, all of which will
// be attached to the same entity
//-----------------------------------------------------------------------
//
BYTE count;
*stream >> count;
#if defined(_ARMOR)
if (!count)
Verify(m_localSpaceBounds.sphereRadius > 0.0f);
#endif
while (count-- > 0)
{
CollisionVolume *obb = new CollisionVolume(entity, stream, version);
Check_Object(obb);
m_children.Add(obb);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume(
Entity *main_entity,
ChildFinder find_function,
Entity *joint_entity,
MemoryStream *stream,
int version
):
Plug(DefaultData),
m_children(NULL)
{
Check_Pointer(this);
Check_Object(main_entity);
Check_Pointer(find_function);
Check_Object(joint_entity);
Check_Object(stream);
//
//--------------------------------
// Read in the bounds and material
//--------------------------------
//
*stream >> m_localSpaceBounds;
Verify(m_localSpaceBounds.axisExtents.x >= 0.0f);
Verify(m_localSpaceBounds.axisExtents.y >= 0.0f);
Verify(m_localSpaceBounds.axisExtents.z >= 0.0f);
*stream >> m_material;
m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
m_worldSpaceBounds.sphereRadius = -1.0f;
//
//---------------------------------------------------------------
// All named OBBs represent parental OBBs and must have no radius
//---------------------------------------------------------------
//
Verify(version > 1);
MString joint_name;
*stream >> joint_name;
if (joint_name)
{
Verify(m_localSpaceBounds.sphereRadius == 0.0f);
joint_entity = (*find_function)(main_entity, joint_name);
#if defined(OBB_BUG)
SPEW((OBB_BUG, "Building null node %s", (char*)joint_name));
#endif
#if defined(LAB_ONLY)
if (!joint_entity)
STOP(("Error - couldn't find joint %s", (char*)joint_name));
#endif
if (joint_entity != main_entity)
{
Check_Object(joint_entity);
Verify(!joint_entity->hierarchicalVolume);
joint_entity->hierarchicalVolume = this;
}
}
//
//----------------------------------
// All unnamed OBBs must have bounds
//----------------------------------
//
else
{
Verify(m_localSpaceBounds.sphereRadius > 0.0f);
#if defined(OBB_BUG)
SPEW((OBB_BUG, "Building %f radius obb node", m_localSpaceBounds.sphereRadius));
#endif
}
m_entity = joint_entity;
Check_Object(m_entity);
//
//------------------------------
// Read in the hierarchy of OBBs
//------------------------------
//
BYTE count;
*stream >> count;
#if defined(_ARMOR)
if (!count)
Verify(m_localSpaceBounds.sphereRadius > 0.0f);
#endif
while (count-- > 0)
{
CollisionVolume *obb =
new CollisionVolume(main_entity, find_function, joint_entity, stream, version);
Check_Object(obb);
m_children.Add(obb);
//
//------------------------------------------------------------------------
// If we don't have a joint name, and the child is attached to our entity,
// go ahead and multiply his bounds into our space
//------------------------------------------------------------------------
//
if (!joint_name && m_entity==obb->m_entity)
{
OBB new_obb;
new_obb.Multiply(obb->m_localSpaceBounds, m_localSpaceBounds.localToParent);
obb->m_localSpaceBounds = new_obb;
}
}
#if defined(OBB_BUG)
if (joint_name.GetLength() > 0)
SPEW((OBB_BUG, "Done building null node %s", (char*)joint_name));
else
SPEW((OBB_BUG, "Done building %f radius obb node", m_localSpaceBounds.sphereRadius));
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::~CollisionVolume()
{
Check_Object(this);
//
//----------------------
// Delete the child OBBs
//----------------------
//
if (m_entity)
{
if (!m_entity->solidVolume && m_entity->hierarchicalVolume == this)
m_entity->hierarchicalVolume = NULL;
}
m_children.DeletePlugs();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::Save(MemoryStream *stream)
{
Check_Object(this);
Check_Object(stream);
//
//---------------------------------
// Save out the bounds and material
//---------------------------------
//
*stream << m_localSpaceBounds;
*stream << m_material;
*stream << MString(GetName());
//
//--------------------------------
// Save out the number of children
//--------------------------------
//
BYTE count = 0;
ChainIteratorOf<CollisionVolume*> children(&m_children);
CollisionVolume *volume;
while ((volume = children.ReadAndNext()) != NULL)
{
Check_Object(volume);
++count;
Verify(count < 255);
}
*stream << count;
//
//----------------------
// Save out the children
//----------------------
//
children.First();
while ((volume = children.ReadAndNext()) != NULL)
{
Check_Object(volume);
volume->Save(stream);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char* CollisionVolume::GetName()
{
Check_Object(this); return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::FindEntityToWorld(
Stuff::LinearMatrix4D *entity_to_world,
Entity *base,
const Stuff::LinearMatrix4D &base_to_world
)
{
Check_Object(this);
Check_Object(base);
//
//--------------------------------------------------------------------------
// If the base is our entity, just copy the base to world into the entity to
// world variable
//--------------------------------------------------------------------------
//
if (base == m_entity)
{
*entity_to_world = base_to_world;
return;
}
//
//--------------------------------------------------------------------------
// Otherwise, we have to concatenate up the entity chain until we get to the
// base entity
//--------------------------------------------------------------------------
//
LinearMatrix4D entity_to_base = m_entity->GetLocalToParent();
Entity *entity = m_entity->GetParentEntity();
while (entity != base)
{
Check_Object(entity);
*entity_to_world = entity_to_base;
entity_to_base.Multiply(*entity_to_world, entity->GetLocalToParent());
entity = entity->GetParentEntity();
}
entity_to_world->Multiply(entity_to_base, base_to_world);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume* CollisionVolume::CastRay(
Entity::CollisionQuery *query,
Entity *base,
const Stuff::LinearMatrix4D &base_to_world
)
{
Check_Object(this);
Check_Object(query);
//
//--------------------------------
// See if the entity can be struck
//--------------------------------
//
if (IsDestroyed())
return NULL;
#if defined(LAB_ONLY)
ElementRenderer::Element *element = m_entity->GetElement();
Check_Object(element);
#endif
//
//-------------------------------
// Figure out where our entity is
//-------------------------------
//
LinearMatrix4D entity_to_world;
FindEntityToWorld(&entity_to_world, base, base_to_world);
m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);
//
//-------------------
// See if we this box
//-------------------
//
Line3D *line = query->m_line;
Check_Object(line);
int axis=0;
Scalar len=-1.0f;
if (m_worldSpaceBounds.sphereRadius > 0.0f)
{
len = line->GetDistanceTo(m_worldSpaceBounds, &axis);
if (len == -1.0 || len > line->m_length)
{
#if defined(LAB_ONLY)
if (Show_Line_vs_OBBs)
{
Verify(element->GetCallbackSet() == Callbacks);
unsigned callback = element->GetCallbackIndex();
if (callback < DrawOBBRejected)
element->SetCallbackIndex(DrawOBBRejected);
}
#endif
return NULL;
}
else
{
#if defined(LAB_ONLY)
if (Show_Line_vs_OBBs)
{
Verify(element->GetCallbackSet() == Callbacks);
unsigned callback = element->GetCallbackIndex();
if (callback < DrawOBBTraversed)
element->SetCallbackIndex(DrawOBBTraversed);
}
#endif
}
}
//
//----------------------------------------------------------
// If we did hit the box, and there are no children, we done
//----------------------------------------------------------
//
ChainIteratorOf<CollisionVolume*> children(&m_children);
if (!children.GetCurrent())
{
Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
query->m_material = static_cast<BYTE>(m_material & e_MaterialMask);
bool exit_now;
if (!(m_material&e_GoodEnough) && m_entity->GetInterestLevel() == Entity::RenderingInterestLevel)
{
RAYCAST_LOGIC("vs. Polygons");
Start_Timer(Poly_Ray_Cast);
exit_now = m_entity->GetElement()->CastRay(query);
Stop_Timer(Poly_Ray_Cast);
Verify(!exit_now || *query->m_normal * query->m_line->m_direction < -SMALL);
}
else
{
Verify(len >= 0.0f);
query->m_line->m_length = len;
//
//-------------------------------------------------------------------------------
// Since we hit the OBB, we have to figure out the new normal. All the collision
// told us was which local-space axis was struck, so lets just go get it from the
// matrix of the world obb
//-------------------------------------------------------------------------------
//
query->m_normal->x = m_worldSpaceBounds.localToParent(axis, 0);
query->m_normal->y = m_worldSpaceBounds.localToParent(axis, 1);
query->m_normal->z = m_worldSpaceBounds.localToParent(axis, 2);
query->m_normal->Normalize(*query->m_normal);
if (*query->m_normal*line->m_direction >= -SMALL)
query->m_normal->Negate(*query->m_normal);
exit_now = true;
Verify(*query->m_normal * query->m_line->m_direction < -SMALL);
}
query->m_data = NULL;
#if defined(LAB_ONLY)
if (Show_Line_vs_OBBs)
{
Verify(element->GetCallbackSet() == Callbacks);
if (!(m_material&e_GoodEnough) && m_entity->GetInterestLevel() == Entity::RenderingInterestLevel)
element->SetCallbackIndex(DrawOBBPolygonsTested);
else
element->SetCallbackIndex(DrawOBBStruck);
}
#endif
if (exit_now)
return this;
}
//
//-------------------------------------------------------------
// Check all our children, and keep the hit that is the closest
//-------------------------------------------------------------
//
CollisionVolume* result = NULL;
CollisionVolume *volume;
while ((volume = children.ReadAndNext()) != NULL)
{
Check_Object(volume);
CollisionVolume *temp = volume->CastRay(query, m_entity, entity_to_world);
if (temp)
result = temp;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume* CollisionVolume::CollideOBB(
CollisionVolume *solid,
Entity *base,
const Stuff::LinearMatrix4D &base_to_world
)
{
Check_Object(this);
Check_Object(solid);
//
//--------------------------------
// See if this is a trivial reject
//--------------------------------
//
if (IsDestroyed())
return NULL;
//
//-------------------------------
// Figure out where our entity is
//-------------------------------
//
LinearMatrix4D entity_to_world;
FindEntityToWorld(&entity_to_world, base, base_to_world);
m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);
//
//-------------------
// See if we this box
//-------------------
//
Check_Object(m_entity);
Check_Object(solid->m_entity);
#if defined(LAB_ONLY)
ElementRenderer::Element *our_element = m_entity->GetElement();
Check_Object(our_element);
ElementRenderer::Element *their_element = solid->m_entity->GetElement();
Check_Object(their_element);
#endif
if (m_worldSpaceBounds.sphereRadius > 0.0f)
{
int axis = solid->m_worldSpaceBounds.FindSeparatingAxis(m_worldSpaceBounds);
if (axis != OBB::NoSeparation)
{
#if defined(LAB_ONLY)
if (Show_OBB_vs_OBBs)
{
Verify(our_element->GetCallbackSet() == Callbacks);
unsigned callback = our_element->GetCallbackIndex();
if (callback < DrawOBBRejected)
our_element->SetCallbackIndex(DrawOBBRejected);
callback = their_element->GetCallbackIndex();
if (callback < DrawOBBRejected)
their_element->SetCallbackIndex(DrawOBBRejected);
}
#endif
return NULL;
}
}
//
//----------------------------------------------------------
// If we did hit the box, and there are no children, we done
//----------------------------------------------------------
//
ChainIteratorOf<CollisionVolume*> children(&m_children);
if (!children.GetCurrent())
{
Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
#if defined(LAB_ONLY)
if (Show_OBB_vs_OBBs)
{
Verify(our_element->GetCallbackSet() == Callbacks);
our_element->SetCallbackIndex(DrawOBBStruck);
their_element->SetCallbackIndex(DrawOBBStruck);
}
#endif
return this;
}
//
//-------------------------------------------------------------
// Check all our children, and keep the hit that is the closest
//-------------------------------------------------------------
//
#if defined(LAB_ONLY)
if (Show_OBB_vs_OBBs)
{
Verify(our_element->GetCallbackSet() == Callbacks);
unsigned callback = our_element->GetCallbackIndex();
if (callback < DrawOBBTraversed)
our_element->SetCallbackIndex(DrawOBBTraversed);
}
#endif
CollisionVolume* result = NULL;
CollisionVolume *volume;
while ((volume = children.ReadAndNext()) != NULL)
{
Check_Object(volume);
CollisionVolume *temp = volume->CollideOBB(solid, m_entity, entity_to_world);
if (temp)
result = temp;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::FindSolidsWithin(
const Stuff::Sphere &sphere,
WithinCallback callback,
Entity *original_caller,
Entity *base,
const Stuff::LinearMatrix4D &base_to_world
)
{
Check_Object(this);
Check_Object(&sphere);
Check_Pointer(callback);
//
//--------------------------------
// See if this is a trivial reject
//--------------------------------
//
if (IsDestroyed())
return;
//
//-------------------------------
// Figure out where our entity is
//-------------------------------
//
LinearMatrix4D entity_to_world;
FindEntityToWorld(&entity_to_world, base, base_to_world);
m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);
//
//---------------------------------
// See if this box is in the sphere
//---------------------------------
//
if (m_worldSpaceBounds.sphereRadius > 0.0f)
{
Sphere us(m_worldSpaceBounds);
if (!sphere.Intersects(us))
return;
}
//
//-----------------------
// Check all our children
//-----------------------
//
ChainIteratorOf<CollisionVolume*> children(&m_children);
if (!children.GetCurrent())
{
Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
(*callback)(this, original_caller);
}
else
{
CollisionVolume *volume;
while ((volume = children.ReadAndNext()) != NULL)
{
Check_Object(volume);
volume->FindSolidsWithin(
sphere,
callback,
original_caller,
m_entity,
entity_to_world
);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int CollisionVolume::ReadOBBVersion(MemoryStream *stream)
{
Check_Object(stream);
//
//------------------------------------------------------------------------
// See if this file has an erf signature. If so, the next int will be the
// version number. If not, assume it is version 1 and rewind the file
//------------------------------------------------------------------------
//
int version = CurrentOBBVersion+1;
int obb_signature;
*stream >> obb_signature;
if (obb_signature == 'OBB#')
*stream >> version;
else
stream->RewindPointer(sizeof(obb_signature));
if (version > CurrentOBBVersion)
STOP(("Application must be rebuilt to use this version of OBB data"));
return version;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::WriteOBBVersion(MemoryStream *stream)
{
Check_Object(stream);
*stream << 'OBB#' << static_cast<int>(CurrentOBBVersion);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBRejectedCallback(
ElementRenderer::CameraElement *camera,
const ElementRenderer::StateChange *state,
int culling_state,
ElementRenderer::Element *element
)
{
Check_Object(camera);
Check_Object(state);
Check_Object(element);
//
//--------------------------------
// Figure out the collision volume
//--------------------------------
//
Entity *entity = Cast_Object(Entity*, element->GetClientData());
CollisionVolume *solid = entity->GetHierarchicalVolume();
if (!solid || Show_OBB_vs_OBBs)
solid = entity->GetSolidVolume();
if (solid)
{
Check_Object(solid);
//
//-----------------
// Draw blue bounds
//-----------------
//
if (solid->m_localSpaceBounds.sphereRadius>0.0f)
{
solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
solid->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
else
{
ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
CollisionVolume *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->m_localSpaceBounds.sphereRadius > 0.0f && child->m_entity == solid->m_entity)
{
child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
child->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
}
}
}
element->SetCallbackIndex(0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBTraversedCallback(
ElementRenderer::CameraElement *camera,
const ElementRenderer::StateChange *state,
int culling_state,
ElementRenderer::Element *element
)
{
Check_Object(camera);
Check_Object(state);
Check_Object(element);
//
//--------------------------------
// Figure out the collision volume
//--------------------------------
//
Entity *entity = Cast_Object(Entity*, element->GetClientData());
CollisionVolume *solid = entity->GetHierarchicalVolume();
if (!solid || Show_OBB_vs_OBBs)
solid = entity->GetSolidVolume();
//
//-------------------------------------------------------------------------
// This branchs is used by non-articulated objects. If there are children,
// draw them first in yellow
//-------------------------------------------------------------------------
//
if (solid)
{
Check_Object(solid);
if (solid->m_localSpaceBounds.sphereRadius > 0.0f)
{
ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
if (children.GetCurrent()==NULL)
{
solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
solid->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
else
{
CollisionVolume *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->m_entity != solid->m_entity)
continue;
Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
child->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
}
}
//
//-----------------------------------------------------------------------
// This branch is used by articulated objects. If this joint was struck,
// only draw our children who are also attached to our entity
//-----------------------------------------------------------------------
//
else
{
ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
CollisionVolume *child;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->m_entity != solid->m_entity)
continue;
//
//---------------------------------------------------------------
// Check each child to see if it has children itself. If not, go
// ahead and draw it
//---------------------------------------------------------------
//
ChainIteratorOf<CollisionVolume*> grandchildren(&child->m_children);
if (!grandchildren.GetCurrent())
{
Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
child->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
else
{
CollisionVolume *grandchild;
while ((grandchild = grandchildren.ReadAndNext()) != NULL)
{
Check_Object(grandchild);
Verify(grandchild->m_localSpaceBounds.sphereRadius > 0.0f);
grandchild->m_worldSpaceBounds.Multiply(grandchild->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
grandchild->m_worldSpaceBounds,
Blue,
camera,
state,
0x3f
);
}
}
}
}
}
element->SetCallbackIndex(0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBStruckCallback(
ElementRenderer::CameraElement *camera,
const ElementRenderer::StateChange *state,
int culling_state,
ElementRenderer::Element *element
)
{
Check_Object(camera);
Check_Object(state);
Check_Object(element);
//
//--------------------------------
// Figure out the collision volume
//--------------------------------
//
Entity *entity = Cast_Object(Entity*, element->GetClientData());
CollisionVolume *solid = entity->GetHierarchicalVolume();
if (!solid || Show_OBB_vs_OBBs)
solid = entity->GetSolidVolume();
//
//-------------------------------------------------------------------------
// This branchs is used by non-articulated objects. If there are children,
// draw them first in yellow
//-------------------------------------------------------------------------
//
if (solid)
{
Check_Object(solid);
if (solid->m_localSpaceBounds.sphereRadius > 0.0f)
{
ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
if (children.GetCurrent()==NULL)
{
solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
solid->m_worldSpaceBounds,
Sparkles[0],
camera,
state,
0x3f
);
}
else
{
CollisionVolume *child;
int sparkle=0;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->m_entity != solid->m_entity)
continue;
Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
child->m_worldSpaceBounds,
Sparkles[(sparkle++)&3],
camera,
state,
0x3f
);
}
}
}
//
//-----------------------------------------------------------------------
// This branch is used by articulated objects. If this joint was struck,
// only draw our children who are also attached to our entity
//-----------------------------------------------------------------------
//
else
{
ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
CollisionVolume *child;
int sparkle = 0;
while ((child = children.ReadAndNext()) != NULL)
{
Check_Object(child);
if (child->m_entity != solid->m_entity)
continue;
//
//---------------------------------------------------------------
// Check each child to see if it has children itself. If not, go
// ahead and draw it
//---------------------------------------------------------------
//
ChainIteratorOf<CollisionVolume*> grandchildren(&child->m_children);
if (!grandchildren.GetCurrent())
{
if (child->m_localSpaceBounds.sphereRadius > 0.0f)
{
child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
child->m_worldSpaceBounds,
Sparkles[(sparkle++)&3],
camera,
state,
0x3f
);
}
}
else
{
CollisionVolume *grandchild;
while ((grandchild = grandchildren.ReadAndNext()) != NULL)
{
Check_Object(grandchild);
Verify(grandchild->m_localSpaceBounds.sphereRadius > 0.0f);
grandchild->m_worldSpaceBounds.Multiply(grandchild->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
grandchild->m_worldSpaceBounds,
Sparkles[(sparkle++)&3],
camera,
state,
0x3f
);
}
}
}
}
}
element->SetCallbackIndex(0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBPolygonsTestedCallback(
ElementRenderer::CameraElement *camera,
const ElementRenderer::StateChange *state,
int culling_state,
ElementRenderer::Element *element
)
{
Check_Object(camera);
Check_Object(state);
Check_Object(element);
//
//--------------------------------
// Figure out the collision volume
//--------------------------------
//
Entity *entity = Cast_Object(Entity*, element->GetClientData());
CollisionVolume *solid = entity->GetHierarchicalVolume();
if (!solid || Show_OBB_vs_OBBs)
solid = entity->GetSolidVolume();
if (solid)
{
Check_Object(solid);
Verify(solid->m_localSpaceBounds.sphereRadius > 0.0f);
solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
ElementRenderer::Element::DrawOBB(
solid->m_worldSpaceBounds,
Green,
camera,
state,
0x3f
);
}
element->SetCallbackIndex(0);
}