Files
TeslaRel410/CODE/RP/MUNGA/BOXSOLID.CPP
T
CydandClaude Fable 5 fdd9ac9d97 Initial import: Tesla Release 4.10 (Tesla:BattleTech & Tesla:Red Planet)
Archival snapshot of the Virtual World Entertainment Tesla cockpit
software, 1994-1996: MUNGA engine and L4 pod layer source (Borland
C++ 5.0), BT/RP game code, and game content (models, audio, maps,
gauges, Division renderer data). Includes third-party libraries:
Division dVS/DPL graphics, HMI SOS audio, WATTCP networking.

Files are preserved byte-for-byte (.gitattributes disables all
line-ending conversion). README.md documents the layout, target
hardware, and toolchain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:21:58 -05:00

1440 lines
39 KiB
C++

//===========================================================================//
// File: boxsolid.cpp //
// Project: MUNGA Brick: Spatializer Library //
// Contents: Implementation details of bounding-box collision subtypes //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 01/11/95 JMA Initial port back to C++ //
//---------------------------------------------------------------------------//
// Copyright (C) 1993-1996, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <munga.hpp>
#pragma hdrstop
#if !defined(BOXSOLID_HPP)
# include <boxsolid.hpp>
#endif
#if !defined(FILEUTIL_HPP)
# include <fileutil.hpp>
#endif
#if !defined(ORIGIN_HPP)
# include <origin.hpp>
#endif
#if !defined(LINMTRX_HPP)
# include <linmtrx.hpp>
#endif
#if !defined(LINE_HPP)
# include <line.hpp>
#endif
#if !defined(NOTATION_HPP)
# include <notation.hpp>
#endif
//#############################################################################
//############################## BoxedSolid #############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid::BoxedSolid(
const ExtentBox &extents,
BoxedSolid::Type type,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
TaggedBoundingBox(extents, owner)
{
Check_Pointer(this);
solidType = type;
materialType = material;
nextSolid = next_solid;
if (nextSolid)
{
Check(nextSolid);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid::BoxedSolid(
const ExtentBox &extents,
Material material,
Simulation *owner,
BoxedSolid *next_solid
):
TaggedBoundingBox(extents, owner)
{
Check_Pointer(this);
solidType = BlockType;
materialType = material;
nextSolid = next_solid;
if (nextSolid)
{
Check(nextSolid);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid::~BoxedSolid()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid *
BoxedSolid::MakeBoxedSolid(
BoxedSolidResource *boxed_solid_resource,
Simulation *owner,
BoxedSolid *next_solid
)
{
BoxedSolid *boxed_solid;
switch(boxed_solid_resource->solidType)
{
case BoxedSolid::BlockType:
boxed_solid =
new BoxedSolid(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::SphereType:
boxed_solid =
new BoxedSphere(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::ConeType:
boxed_solid =
new BoxedCone(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::ReducibleBlockType:
boxed_solid =
new BoxedReducibleBlock(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::RampFacingNegativeZType:
boxed_solid =
new BoxedRampFacingNegativeZ(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::RampFacingNegativeXType:
boxed_solid =
new BoxedRampFacingNegativeX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::RampFacingPositiveZType:
boxed_solid =
new BoxedRampFacingPositiveZ(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::RampFacingPositiveXType:
boxed_solid =
new BoxedRampFacingPositiveX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::InvertedRampFacingNegativeZType:
boxed_solid =
new BoxedInvertedRampFacingNegativeZ(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::InvertedRampFacingNegativeXType:
boxed_solid =
new BoxedInvertedRampFacingNegativeX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::InvertedRampFacingPositiveZType:
boxed_solid =
new BoxedInvertedRampFacingPositiveZ(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::InvertedRampFacingPositiveXType:
boxed_solid =
new BoxedInvertedRampFacingPositiveX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::WedgeFacingNegativeZAndPositiveXType:
boxed_solid =
new BoxedWedgeFacingNegativeZAndPositiveX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::WedgeFacingNegativeZAndNegativeXType:
boxed_solid =
new BoxedWedgeFacingNegativeZAndNegativeX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::WedgeFacingPositiveZAndNegativeXType:
boxed_solid =
new BoxedWedgeFacingPositiveZAndNegativeX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::WedgeFacingPositiveZAndPositiveXType:
boxed_solid =
new BoxedWedgeFacingPositiveZAndPositiveX(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::XAxisCylinderType:
boxed_solid =
new BoxedXAxisCylinder(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::YAxisCylinderType:
boxed_solid =
new BoxedYAxisCylinder(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
case BoxedSolid::ZAxisCylinderType:
boxed_solid =
new BoxedZAxisCylinder(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid
);
break;
#if 0
case BoxedSolid::RightHandedTileType:
{
TileResource *terrain =
(TileResource*)boxed_solid_resource;
boxed_solid =
new RightHandedTile(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid,
terrain->cornerHeight
);
}
break;
case BoxedSolid::LeftHandedTileType:
{
TileResource *terrain =
(TileResource*)boxed_solid_resource;
boxed_solid =
new LeftHandedTile(
boxed_solid_resource->solidExtents,
boxed_solid_resource->materialType,
owner,
next_solid,
terrain->cornerHeight
);
}
break;
#endif
#if defined(LAB_ONLY)
default:
Dump(boxed_solid_resource->solidType);
Fail("Bad box type!");
#endif
}
Check(boxed_solid);
return boxed_solid;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
#if DEBUG_LEVEL>0
BoxedSolid::VerifyCollision(BoxedSolidCollision &collision)
#else
BoxedSolid::VerifyCollision(BoxedSolidCollision &)
#endif
{
Check(this);
Check(&collision);
Verify(minX <= collision.collisionSlice.minX);
Verify(maxX >= collision.collisionSlice.maxX);
Verify(minY <= collision.collisionSlice.minY);
Verify(maxY >= collision.collisionSlice.maxY);
Verify(minZ <= collision.collisionSlice.minZ);
Verify(maxZ >= collision.collisionSlice.maxZ);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedSolid::ProcessCollision(
BoxedSolidCollision &,
const Vector3D &,
BoxedSolidCollisionList *,
Normal *,
Scalar *
)
{
Fail("Unsupported mover collision type!\n");
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedSolid::TestInstance() const
{
return solidType == BlockType;
}
//#############################################################################
//########################## BoxedSolidResource #########################
//#############################################################################
static const BoxedSolid::Type
Left_90[BoxedSolid::SolidTypeCount]=
{
BoxedSolid::BlockType, BoxedSolid::SphereType,
BoxedSolid::ConeType, BoxedSolid::ReducibleBlockType,
BoxedSolid::RampFacingNegativeXType,
BoxedSolid::RampFacingPositiveZType,
BoxedSolid::RampFacingPositiveXType,
BoxedSolid::RampFacingNegativeZType,
BoxedSolid::InvertedRampFacingNegativeXType,
BoxedSolid::InvertedRampFacingPositiveZType,
BoxedSolid::InvertedRampFacingPositiveXType,
BoxedSolid::InvertedRampFacingNegativeZType,
BoxedSolid::WedgeFacingNegativeZAndNegativeXType,
BoxedSolid::WedgeFacingPositiveZAndNegativeXType,
BoxedSolid::WedgeFacingPositiveZAndPositiveXType,
BoxedSolid::WedgeFacingNegativeZAndPositiveXType,
BoxedSolid::ZAxisCylinderType,
BoxedSolid::YAxisCylinderType,
BoxedSolid::XAxisCylinderType,
BoxedSolid::LeftHandedTileType,
BoxedSolid::RightHandedTileType
};
static const BoxedSolid::Type
Left_180[BoxedSolid::SolidTypeCount]=
{
BoxedSolid::BlockType, BoxedSolid::SphereType,
BoxedSolid::ConeType, BoxedSolid::ReducibleBlockType,
BoxedSolid::RampFacingPositiveZType,
BoxedSolid::RampFacingPositiveXType,
BoxedSolid::RampFacingNegativeZType,
BoxedSolid::RampFacingNegativeXType,
BoxedSolid::InvertedRampFacingPositiveZType,
BoxedSolid::InvertedRampFacingPositiveXType,
BoxedSolid::InvertedRampFacingNegativeZType,
BoxedSolid::InvertedRampFacingNegativeXType,
BoxedSolid::WedgeFacingPositiveZAndNegativeXType,
BoxedSolid::WedgeFacingPositiveZAndPositiveXType,
BoxedSolid::WedgeFacingNegativeZAndPositiveXType,
BoxedSolid::WedgeFacingNegativeZAndNegativeXType,
BoxedSolid::XAxisCylinderType,
BoxedSolid::YAxisCylinderType,
BoxedSolid::ZAxisCylinderType,
BoxedSolid::RightHandedTileType,
BoxedSolid::LeftHandedTileType
};
static const BoxedSolid::Type
Left_270[BoxedSolid::SolidTypeCount]=
{
BoxedSolid::BlockType, BoxedSolid::SphereType,
BoxedSolid::ConeType, BoxedSolid::ReducibleBlockType,
BoxedSolid::RampFacingPositiveXType,
BoxedSolid::RampFacingNegativeZType,
BoxedSolid::RampFacingNegativeXType,
BoxedSolid::RampFacingPositiveZType,
BoxedSolid::InvertedRampFacingPositiveXType,
BoxedSolid::InvertedRampFacingNegativeZType,
BoxedSolid::InvertedRampFacingNegativeXType,
BoxedSolid::InvertedRampFacingPositiveZType,
BoxedSolid::WedgeFacingPositiveZAndPositiveXType,
BoxedSolid::WedgeFacingNegativeZAndPositiveXType,
BoxedSolid::WedgeFacingNegativeZAndNegativeXType,
BoxedSolid::WedgeFacingPositiveZAndNegativeXType,
BoxedSolid::ZAxisCylinderType,
BoxedSolid::YAxisCylinderType,
BoxedSolid::XAxisCylinderType,
BoxedSolid::LeftHandedTileType,
BoxedSolid::RightHandedTileType
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoxedSolidResource::Instance(
const BoxedSolidResource &source,
const Origin& origin
)
{
materialType = source.materialType;
//
//-------------------
// Handle no rotation
//-------------------
//
if (Close_Enough(origin.angularPosition.w, 1.0f))
{
solidType = source.solidType;
solidExtents = source.solidExtents;
sliceExtents = source.sliceExtents;
}
//
//-------------------------------
// Test for a 180 degree rotation
//-------------------------------
//
else if (Close_Enough(fabs(origin.angularPosition.y), 1.0f))
{
solidType = Left_180[source.solidType];
solidExtents.minX = -source.solidExtents.maxX;
solidExtents.maxX = -source.solidExtents.minX;
solidExtents.minY = source.solidExtents.minY;
solidExtents.maxY = source.solidExtents.maxY;
solidExtents.minZ = -source.solidExtents.maxZ;
solidExtents.maxZ = -source.solidExtents.minZ;
sliceExtents.minX = -source.sliceExtents.maxX;
sliceExtents.maxX = -source.sliceExtents.minX;
sliceExtents.minY = source.sliceExtents.minY;
sliceExtents.maxY = source.sliceExtents.maxY;
sliceExtents.minZ = -source.sliceExtents.maxZ;
sliceExtents.maxZ = -source.sliceExtents.minZ;
}
//
//---------------------------------
// Handle a 90 degree left rotation
//---------------------------------
//
else if (Close_Enough(origin.angularPosition.w, origin.angularPosition.y))
{
solidType = Left_90[source.solidType];
solidExtents.minX = source.solidExtents.minZ;
solidExtents.maxX = source.solidExtents.maxZ;
solidExtents.minY = source.solidExtents.minY;
solidExtents.maxY = source.solidExtents.maxY;
solidExtents.minZ = -source.solidExtents.maxX;
solidExtents.maxZ = -source.solidExtents.minX;
sliceExtents.minX = source.sliceExtents.minZ;
sliceExtents.maxX = source.sliceExtents.maxZ;
sliceExtents.minY = source.sliceExtents.minY;
sliceExtents.maxY = source.sliceExtents.maxY;
sliceExtents.minZ = -source.sliceExtents.maxX;
sliceExtents.maxZ = -source.sliceExtents.minX;
}
//
//----------------------------------
// Handle a 90 degree right rotation
//----------------------------------
//
else if (Close_Enough(origin.angularPosition.w, -origin.angularPosition.y))
{
solidType = Left_270[source.solidType];
solidExtents.minX = -source.solidExtents.maxZ;
solidExtents.maxX = -source.solidExtents.minZ;
solidExtents.minY = source.solidExtents.minY;
solidExtents.maxY = source.solidExtents.maxY;
solidExtents.minZ = source.solidExtents.minX;
solidExtents.maxZ = source.solidExtents.maxX;
sliceExtents.minX = -source.sliceExtents.maxZ;
sliceExtents.maxX = -source.sliceExtents.minZ;
sliceExtents.minY = source.sliceExtents.minY;
sliceExtents.maxY = source.sliceExtents.maxY;
sliceExtents.minZ = source.sliceExtents.minX;
sliceExtents.maxZ = source.sliceExtents.maxX;
}
//
//-------------------------------------------------------------------------
// Handle an arbitrary rotation. Make sure that only vertically symetrical
// solids get here
//-------------------------------------------------------------------------
//
else if (
source.solidType == BoxedSolid::YAxisCylinderType
|| source.solidType == BoxedSolid::ConeType
|| source.solidType == BoxedSolid::SphereType
)
{
solidType = source.solidType;
Verify(source.solidExtents.minX == source.sliceExtents.minX);
Verify(source.solidExtents.maxX == source.sliceExtents.maxX);
Verify(source.solidExtents.minY == source.sliceExtents.minY);
Verify(source.solidExtents.maxY == source.sliceExtents.maxY);
Verify(source.solidExtents.minZ == source.sliceExtents.minZ);
Verify(source.solidExtents.maxZ == source.sliceExtents.maxZ);
Vector3D center;
center.x = (source.solidExtents.minX + source.solidExtents.maxX) * 0.5f;
center.y = source.solidExtents.minY;
center.z = (source.solidExtents.minZ + source.solidExtents.maxZ) * 0.5f;
Scalar radius = center.x - source.solidExtents.minX;
LinearMatrix m;
m = origin.angularPosition;
Vector3D translated;
translated.Multiply(center, m);
solidExtents.minX = translated.x - radius;
solidExtents.maxX = translated.x + radius;
solidExtents.minY = source.solidExtents.minY;
solidExtents.maxY = source.solidExtents.maxY;
solidExtents.minZ = translated.z - radius;
solidExtents.maxZ = translated.z + radius;
sliceExtents = solidExtents;
}
//
//----------------
// Otherwise, barf
//----------------
//
#if defined(LAB_ONLY)
else
{
Fail("Illegal solid for a non-90 degree rotation!\n");
}
#endif
solidExtents.minX += origin.linearPosition.x;
solidExtents.maxX += origin.linearPosition.x;
solidExtents.minY += origin.linearPosition.y;
solidExtents.maxY += origin.linearPosition.y;
solidExtents.minZ += origin.linearPosition.z;
solidExtents.maxZ += origin.linearPosition.z;
sliceExtents.minX += origin.linearPosition.x;
sliceExtents.maxX += origin.linearPosition.x;
sliceExtents.minY += origin.linearPosition.y;
sliceExtents.maxY += origin.linearPosition.y;
sliceExtents.minZ += origin.linearPosition.z;
sliceExtents.maxZ += origin.linearPosition.z;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TileResource::Instance(
const TileResource &source,
const Origin& origin
)
{
BoxedSolidResource::Instance(source, origin);
//
//-------------------------------
// Test for a 180 degree rotation
//-------------------------------
//
if (Close_Enough(fabs(origin.angularPosition.y), 1.0f))
{
cornerHeight[0] = source.cornerHeight[3];
cornerHeight[1] = source.cornerHeight[2];
cornerHeight[2] = source.cornerHeight[1];
cornerHeight[3] = source.cornerHeight[0];
}
//
//---------------------------------
// Handle a 90 degree left rotation
//---------------------------------
//
else if (Close_Enough(origin.angularPosition.w, origin.angularPosition.y))
{
cornerHeight[0] = source.cornerHeight[1];
cornerHeight[1] = source.cornerHeight[3];
cornerHeight[2] = source.cornerHeight[0];
cornerHeight[3] = source.cornerHeight[2];
}
//
//----------------------------------
// Handle a 90 degree right rotation
//----------------------------------
//
else if (Close_Enough(origin.angularPosition.w, -origin.angularPosition.y))
{
cornerHeight[0] = source.cornerHeight[2];
cornerHeight[1] = source.cornerHeight[0];
cornerHeight[2] = source.cornerHeight[3];
cornerHeight[3] = source.cornerHeight[1];
}
}
//#############################################################################
ResourceDescription::ResourceID
BoxedSolidResource::CreateBoxedSolidStream(
const char *entry_data,
ResourceFile *file,
const ResourceDirectories *resource_directories,
Logical convert_boxes
)
{
char *filename =
MakePathedFilename(resource_directories->collisionDirectory, entry_data);
Register_Pointer(filename);
NotationFile *collision_file = new NotationFile(filename);
Unregister_Pointer(filename);
delete filename;
Register_Object(collision_file);
ResourceDescription::ResourceID collision_ID;
if (collision_file->PageCount())
{
// LAB_ONLY |= collision_file->IsMarkedLabOnly();
BoxedSolidList *box_solid_list = new BoxedSolidList;
Register_Object(box_solid_list);
int size = box_solid_list->AddBoxedSolids(collision_file);
BoxedSolidResource *boxed_solid_resource =
new BoxedSolidResource[size];
Register_Pointer(boxed_solid_resource);
int j = 0;
BoundingBoxListNode *p;
for (p = box_solid_list->GetRoot(); p; p = p->GetNextNode())
{
Verify(j < size);
Check(p);
BoxedSolid *box = (BoxedSolid *)p->GetBoundingBox();
Check(box);
boxed_solid_resource[j].materialType = box->materialType;
if ((box->solidType == BoxedSolid::BlockType) && (convert_boxes))
{
boxed_solid_resource[j].solidType = BoxedSolid::ReducibleBlockType;
}
else
{
boxed_solid_resource[j].solidType = box->solidType;
}
boxed_solid_resource[j].solidExtents = *box;
boxed_solid_resource[j].sliceExtents = p->solidSlice;
boxed_solid_resource[j].recordLength = sizeof(boxed_solid_resource[j]);
++j;
}
for (p = box_solid_list->GetRoot(); p; p = p->GetNextNode())
{
Check(p);
BoxedSolid *box = (BoxedSolid *)p->GetBoundingBox();
Check(box);
Unregister_Object(box);
delete(box);
}
ResourceDescription *res_description = file->FindResourceDescription(entry_data,
ResourceDescription::BoxedSolidStreamResourceType);
if(res_description == NULL)
{
collision_ID =
file->AddResource(
entry_data,
ResourceDescription::BoxedSolidStreamResourceType,
1,
ResourceDescription::Preload,
boxed_solid_resource,
size * sizeof(BoxedSolidResource)
)->resourceID;
}
else
{
collision_ID = res_description->resourceID;
}
Unregister_Pointer(boxed_solid_resource);
delete boxed_solid_resource;
Unregister_Object(box_solid_list);
delete box_solid_list;
}
else
{
//----------------------------------------------
// display warning that collision file is empty
//----------------------------------------------
DEBUG_STREAM << "** Collision file '" << entry_data <<
"' empty or not found. **" << endl;
//----------------------------------------------
// continue to show all missing collision files
//----------------------------------------------
}
Unregister_Object(collision_file);
delete collision_file;
return collision_ID;
}
//#############################################################################
//########################## BoxedSolidCollision ########################
//#############################################################################
static const int
Cant_Occlude[BoxedSolid::SolidTypeCount]=
{
0, 1<<(X_Axis|Y_Axis|Z_Axis), 1<<(X_Axis|Z_Axis), 0,
1<<X_Axis, 1<<Z_Axis, 1<<X_Axis, 1<<Z_Axis,
1<<X_Axis, 1<<Z_Axis, 1<<X_Axis, 1<<Z_Axis,
1<<Y_Axis, 1<<Y_Axis, 1<<Y_Axis, 1<<Y_Axis,
1<<X_Axis, 1<<Y_Axis, 1<<Z_Axis,
0, 0
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedSolidCollision::Occludes(
BoxedSolidCollision &collision,
const Vector3D &velocity
)
{
Check(this);
Check(&collision);
Check(&velocity);
//
//--------------------------------------------------------------------------
// Check against the three major axes of motion, making sure that the VTV is
// actually moving in that component
//--------------------------------------------------------------------------
//
for (int axis=X_Axis; axis<=Z_Axis; ++axis)
{
if (Small_Enough(velocity[axis]))
{
continue;
}
//
//-----------------------------------------------------------------------
// If we cannot guarantee occlusion, do some more testing to see if it is
// possible to guarantee occlusion on a model-specific basis. This is
// simply to handle transversal motion across the ramp
//
// NOTE: This is a KLUDGE!!! This will not properly handle two ramps side
// by side of different sizes where the smaller ramp is in the
// empty portion of the larger ramp
//-----------------------------------------------------------------------
//
BoxedSolid *first = GetTreeVolume();
BoxedSolid *second = collision.GetTreeVolume();
Check(first);
Check(second);
if (
(Cant_Occlude[first->solidType] & (1<<axis))
&& first->solidType != second->solidType
)
{
continue;
}
//
//----------------------------------------------------------------------
// Figure out what face to check against, and make sure that the face of
// the possibly occluding slice is nearer than the other slice
//----------------------------------------------------------------------
//
int face = (axis<<1) + (velocity[axis] < 0.0f);
int opposite_face = face^1;
if (face&1)
{
if (first->solidType == BoxedSolid::BlockType)
{
if (collisionSlice[face] <= collision.collisionSlice[face])
{
continue;
}
}
else if (
collisionSlice[opposite_face] < collision.collisionSlice[face]
)
{
continue;
}
}
else
{
if (first->solidType == BoxedSolid::BlockType)
{
if (collisionSlice[face] >= collision.collisionSlice[face])
{
continue;
}
}
else if (
collisionSlice[opposite_face] > collision.collisionSlice[face]
)
{
continue;
}
}
//
//------------------------------------------------------------------
// Check the faces on the other axes to make sure that the occluding
// object at least covers the extent of the occluded object
//------------------------------------------------------------------
//
for (face=0; face<6; ++face)
{
if ((face>>1) == axis)
{
continue;
}
if (face&1)
{
if (collisionSlice[face] < collision.collisionSlice[face])
{
break;
}
}
else if (collisionSlice[face] > collision.collisionSlice[face])
{
break;
}
}
//
//-----------------------------------------------------------------------
// If everything checked out, then List[i] occludes List[j]. Stretch the
// definition of List[i] to include the slice occluded if necessary
//-----------------------------------------------------------------------
//
if (face == 6)
{
face = axis<<1;
if (velocity[axis] > 0.0f)
{
++face;
collisionSlice[face] =
Max(collisionSlice[face], collision.collisionSlice[face]);
}
else
{
collisionSlice[face] =
Min(collisionSlice[face], collision.collisionSlice[face]);
}
return True;
}
}
return False;
}
//#############################################################################
//######################## BoxedSolidCollisionList ######################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoxedSolidCollisionList::ReduceCollisionList(const Vector3D &velocity)
{
Check(this);
Check(&velocity);
int
i,j;
//
//--------------------------------------------------------------------------
// Fuse the collision slices together into the largest possible chunks based
// upon the collision model of each of the involved slices. Repeat until no
// fusings were made in the last pass or only one collision slice remains
//--------------------------------------------------------------------------
//
Logical again = True;
int collisions = GetCollisionCount();
phantomCollisions = 0;
while (again && collisions>1)
{
again = False;
//
//--------------------------------------------------------------------
// Check each collision slice against the remaining slices in the list
//--------------------------------------------------------------------
//
for (i=0; i<collisions; ++i)
{
for (j=i+1; j<collisions; ++j)
{
//
//-------------------------------------------------------------
// If the model types are different, these two slices cannot be
// fused
//-------------------------------------------------------------
//
BoxedSolidCollision *first = &(*this)[i];
BoxedSolidCollision *second = &(*this)[j];
if (
first->GetTreeVolume()->solidType
!= second->GetTreeVolume()->solidType
)
{
continue;
}
//
//----------------------------------------------------
// Make sure that the faces on two sets of sides match
//----------------------------------------------------
//
int matches = 0;
int face = -1;
for (int side=0; side<6; side += 2)
{
if (
first->collisionSlice[side] == second->collisionSlice[side]
&& first->collisionSlice[side+1]
== second->collisionSlice[side+1]
)
{
++matches;
}
else if (face<0)
{
face = side;
}
}
if (matches != 2)
{
continue;
}
//
//----------------------------------------------------------------
// Check to make sure that the two slices have an opposing face in
// common, which will allow the slices to be fused
//----------------------------------------------------------------
//
if (
first->collisionSlice[face] != second->collisionSlice[face+1]
&& first->collisionSlice[face+1]
!= second->collisionSlice[face]
)
{
continue;
}
//
//----------------------------------------------------------------
// Find the face to fuse, then find out which solid description to
// use based upon the velocity vector
//----------------------------------------------------------------
//
if (first->collisionSlice[face+1] == second->collisionSlice[face])
{
++face;
}
BoxedSolid *original = NULL;
if (
first->GetTreeVolume() != second->GetTreeVolume()
&& second->Occludes(*first, velocity)
)
{
original = first->GetTreeVolume();
first->treeVolume = second->GetTreeVolume();
}
//
//-------------------------
// Fuse the blocks together
//-------------------------
//
first->collisionSlice[face] = second->collisionSlice[face];
//
//-----------------------------------------------
// Erase the second slice from the collision list
//-----------------------------------------------
//
for (int k=j+1; k<collisions; ++k)
listStart[k-1] = listStart[k];
++phantomCollisions;
listStart[--collisions].treeVolume = original;
--j;
again = True;
}
}
}
//
//-----------------------------------------------------------------------
// Check to see if any of the collisions occlude one another if more than
// one collision slice remains
//-----------------------------------------------------------------------
//
if (collisions>1)
{
//
//--------------------------
// Erase the occlusion table
//--------------------------
//
Logical* occluded = new Logical[collisions];
Register_Pointer(occluded);
BoxedSolid **volumes = new BoxedSolid* [collisions];
Register_Pointer(volumes);
for (i=0; i<collisions; ++i)
occluded[i] = False;
//
//------------------------------------------------------------
// Check each slice to see if it is occluded by something else
//------------------------------------------------------------
//
for (i=0; i<collisions; ++i)
{
for (j=0; j<collisions; ++j)
{
if (i==j || occluded[j])
{
continue;
}
if ((*this)[i].Occludes((*this)[j], velocity))
{
occluded[j] = True;
}
}
}
//
//---------------------------------------------------------
// Check the occlusion table, and erase any occluded slices
//---------------------------------------------------------
//
j = 0;
int occludeds = 0;
for (i=0; i<collisions; ++i)
{
if (!occluded[i])
{
if (i>j)
{
listStart[j] = listStart[i];
}
++j;
}
else
{
volumes[++occludeds] = (BoxedSolid*)listStart[i].treeVolume;
}
}
i = 0;
while (j<collisions)
{
listStart[j++].treeVolume = volumes[i++];
}
phantomCollisions += occludeds;
Unregister_Pointer(volumes);
delete[] volumes;
Unregister_Pointer(occluded);
delete[] occluded;
}
}
//#############################################################################
//########################## BoxedReducibleBlock ########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedReducibleBlock::BoxedReducibleBlock(
const ExtentBox &extents,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
BoxedSolid(extents, ReducibleBlockType, material, owner, next_solid)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedReducibleBlock::~BoxedReducibleBlock()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedReducibleBlock::TestInstance() const
{
return solidType == ReducibleBlockType;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedReducibleBlock::IntersectsBounded(const ExtentBox &extents)
{
Check(this);
Check(&extents);
return ExtentBox::Intersects(extents);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedReducibleBlock::ContainsBounded(const Point3D &point)
{
Check(this);
Check(&point);
return ExtentBox::Contains(point);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BoxedReducibleBlock::FindDistanceBelowBounded(const Point3D &point)
{
Check(this);
Check(&point);
if (
minX <= point.x && maxX >= point.x && minY <= point.y
&& minZ <= point.z && maxZ >= point.z
)
{
Scalar result = point.y - maxY;
return Max(result, 0.0f);
}
else
{
return -1.0f;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedReducibleBlock::HitByBounded(
Line *line,
Scalar enters,
Scalar leaves
)
{
Check(this);
Check(line);
Scalar
perpendicular,
drift,
distance;
//
//--------------------
// Set up for the test
//--------------------
//
for (int i=0; i<6; ++i)
{
//
//--------------------------------------------------------------------
// Figure out what axis we are dealing with, then based upon the
// direction of the face, find out the distance from the origin to the
// place against the normal, and find out how fast the perpendicular
// distance changes with a unit movement along the line
//--------------------------------------------------------------------
//
int face = i;
int axis = face >> 1;
if (face&1)
{
perpendicular = line->origin[axis] - (*this)[face];
drift = line->direction[axis];
}
else
{
perpendicular = (*this)[face] - line->origin[axis];
drift = -line->direction[axis];
}
//
//-------------------------------------------------------------------
// If the line is parallel to the face, figure out whether or not the
// line origin lies within the face's half-space
//-------------------------------------------------------------------
//
if (Small_Enough(drift))
{
if (perpendicular > 0.0f)
{
return False;
}
else
{
continue;
}
}
//
//--------------------------------------------------------------------
// If the drift is towards the plane's halfspace, this plane is one of
// the one through which the line could enter the node
//--------------------------------------------------------------------
//
distance = -perpendicular / drift;
if (drift < 0.0f)
{
if (distance > enters)
{
enters = distance;
}
if (enters > line->length)
{
return False;
}
}
//
//--------------------------------------------------------------------
// If the drift is towards the plane's halfspace, this plane is one of
// the one through which the line could enter the node
//--------------------------------------------------------------------
//
else
{
if (distance < leaves)
{
leaves = distance;
}
if (leaves < 0.0f)
{
return False;
}
}
}
//
//-----------------------------------------------------------------------
// If we exit the loop, then make sure that we actually hit the interior,
// and let the box figure out if the what happens inside it
//-----------------------------------------------------------------------
//
if (enters <= leaves)
{
line->length = Max(enters, 0.0f);
return True;
}
else
{
return False;
}
}
//#############################################################################
//########################### BoxedSolidList ############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
BoxedSolidList::AddBoxedSolids(NotationFile *notation_file)
{
char page_name[50];
int solidnumber = 0;
int type;
BoxedSolidResource boxed_solid_resource;
BoxedSolid *boxed_solid;
if (!notation_file->GetEntry("COLLISION", "count", &solidnumber))
{
DEBUG_STREAM << "No 'COLLISION' entry!!!!!" << endl;
Exit(AbortExitCodeID);
}
for (int j=0; j < solidnumber; ++j)
{
sprintf(page_name, "volume %d", j);
if (!notation_file->GetEntry(page_name, "TypeNumber", &type))
{
DEBUG_STREAM << "No solid at collision number : " << j << endl;
Exit(AbortExitCodeID);
}
boxed_solid_resource.solidType = (BoxedSolid::Type)type;
if (!notation_file->GetEntry(page_name, "MaterialNumber", &type))
{
DEBUG_STREAM << "No material at collision number : " << j << endl;
Exit(AbortExitCodeID);
}
boxed_solid_resource.materialType = (BoxedSolid::Material)type;
int errorvalue = 0;
errorvalue |= !notation_file->GetEntry(page_name, "extminX", &(boxed_solid_resource.solidExtents.minX));
errorvalue |= !notation_file->GetEntry(page_name, "extmaxX", &(boxed_solid_resource.solidExtents.maxX));
errorvalue |= !notation_file->GetEntry(page_name, "extminY", &(boxed_solid_resource.solidExtents.minY));
errorvalue |= !notation_file->GetEntry(page_name, "extmaxY", &(boxed_solid_resource.solidExtents.maxY));
errorvalue |= !notation_file->GetEntry(page_name, "extminZ", &(boxed_solid_resource.solidExtents.minZ));
errorvalue |= !notation_file->GetEntry(page_name, "extmaxZ", &(boxed_solid_resource.solidExtents.maxZ));
if ( errorvalue )
{
DEBUG_STREAM << "Collision number : " << j
<< " does not have valid extents\n";
Exit(AbortExitCodeID);
}
errorvalue = 0;
errorvalue |= !notation_file->GetEntry(page_name, "slcminX", &(boxed_solid_resource.sliceExtents.minX));
errorvalue |= !notation_file->GetEntry(page_name, "slcmaxX", &(boxed_solid_resource.sliceExtents.maxX));
errorvalue |= !notation_file->GetEntry(page_name, "slcminY", &(boxed_solid_resource.sliceExtents.minY));
errorvalue |= !notation_file->GetEntry(page_name, "slcmaxY", &(boxed_solid_resource.sliceExtents.maxY));
errorvalue |= !notation_file->GetEntry(page_name, "slcminZ", &(boxed_solid_resource.sliceExtents.minZ));
errorvalue |= !notation_file->GetEntry(page_name, "slcmaxZ", &(boxed_solid_resource.sliceExtents.maxZ));
if ( errorvalue )
{
DEBUG_STREAM << "Collision number : " << j
<< " does not have valid slice extents\n";
Exit(AbortExitCodeID);
}
boxed_solid =
BoxedSolid::MakeBoxedSolid(&boxed_solid_resource, NULL, NULL);
Register_Object(boxed_solid);
Add(boxed_solid, boxed_solid_resource.sliceExtents);
}
return solidnumber;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(TEST_CLASS)
# include "boxsolid.tcp"
#endif