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>
496 lines
13 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|