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.
1261 lines
34 KiB
C++
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);
|
|
}
|