Files
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

496 lines
13 KiB
C++

//===========================================================================//
// File: boxsolid.cc //
// 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-1995, 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(VECTOR3D_HPP)
# include <vector3d.hpp>
#endif
//#############################################################################
//########################### BoxedSolidList ############################
//#############################################################################
enum {
X_Axis_Bit = 1,
Y_Axis_Bit = 2,
Z_Axis_Bit = 4
};
enum {
Min_Side = 0x001,
Inside = 0x002,
Max_Side = 0x004,
Both_Sides = Min_Side|Max_Side,
Shift = 3,
X_Shift = 2 * Shift,
Min_X_Side = Min_Side << X_Shift,
Inside_X = Inside << X_Shift,
Max_X_Side = Max_Side << X_Shift,
X_Mask = Min_X_Side|Inside_X|Max_X_Side,
Y_Shift = Shift,
Min_Y_Side = Min_Side << Y_Shift,
Inside_Y = Inside << Y_Shift,
Max_Y_Side = Max_Side << Y_Shift,
Y_Mask = Min_Y_Side|Inside_Y|Max_Y_Side,
Z_Shift = 0,
Min_Z_Side = Min_Side << Z_Shift,
Inside_Z = Inside << Z_Shift,
Max_Z_Side = Max_Side << Z_Shift,
Z_Mask = Min_Z_Side|Inside_Z|Max_Z_Side
};
int
Legal_To_Fuse[BoxedSolid::SolidTypeCount]=
{
X_Axis_Bit|Y_Axis_Bit|Z_Axis_Bit, 0, 0, 0,
X_Axis_Bit, Z_Axis_Bit, X_Axis_Bit, Z_Axis_Bit,
X_Axis_Bit, Z_Axis_Bit, X_Axis_Bit, Z_Axis_Bit,
Y_Axis_Bit, Y_Axis_Bit, Y_Axis_Bit, Y_Axis_Bit,
X_Axis_Bit, Y_Axis_Bit, Z_Axis_Bit
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::Reduce()
{
Check(this);
BoundingBoxListNode
*i, *j, *previous;
//
//--------------------------------------------------------------------------
// 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;
while (again)
{
again = False;
//
//--------------------------------------------------------------------
// Check each collision slice against the remaining slices in the list
//--------------------------------------------------------------------
//
for (i=root; i; i = i->previousNode)
{
Check(i);
previous = i;
j = i->previousNode;
while (j)
{
Check(j);
//
//-------------------------------------------------------------
// If the model types are different, these two slices cannot be
// fused
//-------------------------------------------------------------
//
BoundingBox *first = i->boundingBox;
BoundingBox *second = j->boundingBox;
//
//----------------------------------------------------
// 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)[side] == (*second)[side]
&& (*first)[side+1] == (*second)[side+1]
)
{
++matches;
}
else if (face<0)
{
face = side;
}
}
if (matches != 2)
{
Next_Solid:
previous = j;
j = j->previousNode;
continue;
}
//
//----------------------------------------------------------------
// Check to make sure that the two solids have an opposing face in
// common, which will allow the solids to be fused
//----------------------------------------------------------------
//
if (
(*first)[face] != (*second)[face+1]
&& (*first)[face+1] != (*second)[face]
)
{
goto Next_Solid;
}
//
//----------------------------------------------------
// Find the face to fuse, and fuse the blocks together
//----------------------------------------------------
//
if ((*first)[face+1] == (*second)[face])
{
++face;
}
(*first)[face] = (*second)[face];
//
//-----------------------------------------------
// Erase the second solid from the collision list
//-----------------------------------------------
//
--nodeCount;
if (previous)
{
previous->previousNode = j->previousNode;
Unregister_Object(j);
delete(j);
j = previous->previousNode;
}
else
{
root = j->previousNode;
Unregister_Object(j);
delete(j);
j = root;
}
again = True;
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::SortForTree()
{
Check(this);
int
i, j;
BoundingBoxListNode
*p, *q;
//
//-----------------------------------------------------------------------
// Find out how many collision solids are in the list, and figure out the
// total extent box so that we can set the traversal order correctly
//-----------------------------------------------------------------------
//
ExtentBox map_extents;
map_extents.minX = 65535.9999f;
map_extents.minY = 65535.9999f;
map_extents.minZ = 65535.9999f;
map_extents.maxX = -65535.9999f;
map_extents.maxY = -65535.9999f;
map_extents.maxZ = -65535.9999f;
for (p=root; p; p = p->previousNode)
{
Check(p);
if (p->boundingBox->minX < map_extents.minX)
{
map_extents.minX = p->boundingBox->minX;
}
if (p->boundingBox->maxX > map_extents.maxX)
{
map_extents.maxX = p->boundingBox->maxX;
}
if (p->boundingBox->minY < map_extents.minY)
{
map_extents.minY = p->boundingBox->minY;
}
if (p->boundingBox->maxY > map_extents.maxY)
{
map_extents.maxY = p->boundingBox->maxY;
}
if (p->boundingBox->minZ < map_extents.minZ)
{
map_extents.minZ = p->boundingBox->minZ;
}
if (p->boundingBox->maxZ > map_extents.maxZ)
{
map_extents.maxZ = p->boundingBox->maxZ;
}
}
if (
map_extents.maxZ - map_extents.minZ
> map_extents.maxX - map_extents.minX
)
{
isXMajorAxis = False;
}
//
//----------------------------------------------------
// Allocate a scoreboard and fill it with sorting info
//----------------------------------------------------
//
Verify(!scoreBoard);
scoreBoard = new int[nodeCount * nodeCount];
Register_Pointer(scoreBoard);
int *score = scoreBoard;
boundingBoxIndex = new BoundingBox* [nodeCount];
Register_Pointer(boundingBoxIndex);
for (p=root,i=0; p; p = p->previousNode,++i)
{
Check(p);
BoundingBox *first = p->boundingBox;
boundingBoxIndex[i] = first;
for (q=root,j=0; q; q = q->previousNode,++j,++score)
{
//
//------------------------------
// Ignore scoring against itself
//------------------------------
//
Check(q);
*score = 0;
if (p == q)
{
continue;
}
BoundingBox *second = q->boundingBox;
//
//--------------------------------------------------------------------
// Step through the three axes and set the flags showing how the
// second solid is split up by the first solid. Make sure that if the
// second solid completely covers the first that this inside bit is
// set correctly
//--------------------------------------------------------------------
//
for (int axis = X_Axis; axis <= Z_Axis; --axis)
{
*score <<= Shift;
int face = axis << 1;
if ((*second)[face] < (*first)[face])
{
*score |= Min_Side;
}
else if ((*second)[face] < (*first)[face+1])
{
*score |= Inside;
}
if ((*second)[face+1] > (*first)[face+1])
{
*score |= Max_Side;
}
else if ((*second)[face+1] > (*first)[face])
{
*score |= Inside;
}
if ((*score & Both_Sides) == Both_Sides)
{
*score |= Inside;
}
}
}
}
//
//--------------------------------------------------------------------------
// Create a new BoxedSolid list for results to go into, and a third in which
// to pass the active list
//--------------------------------------------------------------------------
//
BoundingBoxList active;
active.root = root;
active.nodeCount = nodeCount;
root = NULL;
BoundingBoxTree tree_so_far;
Sort(
active,
map_extents,
tree_so_far
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::Sort(
BoundingBoxList &,//active,
ExtentBox &,//map_extents,
BoundingBoxTree &//tree_so_far
)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoxedSolidList::Reduce()
{
Check(this);
BoundingBoxListNode
*i, *j, *previous;
//
//--------------------------------------------------------------------------
// 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;
while (again)
{
again = False;
//
//--------------------------------------------------------------------
// Check each collision slice against the remaining slices in the list
//--------------------------------------------------------------------
//
for (i=root; i; i = i->previousNode)
{
Check(i);
previous = i;
j = i->previousNode;
while (j)
{
Check(j);
//
//-------------------------------------------------------------
// If the model types are different, these two slices cannot be
// fused
//-------------------------------------------------------------
//
BoxedSolid *first = Cast_Object(BoxedSolid*, i->boundingBox);
BoxedSolid *second = Cast_Object(BoxedSolid*, j->boundingBox);
if (first->solidType != second->solidType)
{
Next_Solid:
previous = j;
j = j->previousNode;
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)[side] == (*second)[side]
&& (*first)[side+1] == (*second)[side+1]
)
{
++matches;
}
else if (face<0)
{
face = side;
}
}
if (matches != 2)
{
goto Next_Solid;
}
//
//----------------------------------------------------------------
// Check to make sure that the two solids have an opposing face in
// common, which will allow the solids to be fused
//----------------------------------------------------------------
//
if (
(*first)[face] != (*second)[face+1]
&& (*first)[face+1] != (*second)[face]
)
{
goto Next_Solid;
}
//
//---------------------------------------------------------------
// Make sure that this type of solid is legal to be fused in this
// direction
//---------------------------------------------------------------
//
if (!(Legal_To_Fuse[first->solidType] & (face>>1)))
{
goto Next_Solid;
}
//
//----------------------------------------------------
// Find the face to fuse, and fuse the blocks together
//----------------------------------------------------
//
if ((*first)[face+1] == (*second)[face])
{
++face;
}
(*first)[face] = (*second)[face];
//
//-----------------------------------------------
// Erase the second solid from the collision list
//-----------------------------------------------
//
if (previous)
{
previous->previousNode = j->previousNode;
Unregister_Object(j);
delete(j);
j = previous->previousNode;
}
else
{
root = j->previousNode;
Unregister_Object(j);
delete(j);
j = root;
}
again = True;
}
}
}
}