Files
firestorm/Gameleap/code/mw4/Tools/MakeBSP.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

787 lines
21 KiB
C++

#include <ElementRenderer\GroupElement.hpp>
#include <ElementRenderer\ListElement.hpp>
#include <ElementRenderer\ShapeElement.hpp>
#include <MLR\MLRShape.hpp>
#include <MLR\MLRIndexedPrimitiveBase.hpp>
#include <Adept\Tile.hpp>
//#define BSP_BUG "jmalbert"
#undef BSP_BUG
extern bool Debug_Tile;
enum {
Inside_Plane = 1,
On_Plane = 2,
Outside_Plane = 4,
Split_Plane = Inside_Plane|Outside_Plane
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
FindPlanes(
ElementRenderer::Element *element,
Stuff::DynamicArrayOf<Stuff::Plane> *planes,
int *planes_filled
)
{
Check_Object(element);
Check_Object(planes);
Check_Pointer(planes_filled);
//
//-----------------------
// Spin through the group
//-----------------------
//
if (element->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData))
{
ElementRenderer::GroupElement *group =
Cast_Object(ElementRenderer::GroupElement *, element);
Stuff::ChainIteratorOf<ElementRenderer::Element*> *children =
group->MakeIterator();
Register_Object(children);
ElementRenderer::Element *child;
while ((child = children->ReadAndNext()) != NULL)
{
Check_Object(child);
FindPlanes(child, planes, planes_filled);
}
Unregister_Object(children);
delete children;
}
//
//----------------------
// Spin through the list
//----------------------
//
else if (element->IsDerivedFrom(ElementRenderer::ListElement::DefaultData))
{
ElementRenderer::ListElement *list =
Cast_Object(ElementRenderer::ListElement *, element);
for (unsigned i=0; i<list->GetActiveCount(); ++i)
{
ElementRenderer::Element *child = list->GetIndexedElement(i);
Check_Object(child);
FindPlanes(child, planes, planes_filled);
}
}
//
//-----------------------
// Spin through the shape
//-----------------------
//
else if (element->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData))
{
ElementRenderer::ShapeElement *shape =
Cast_Object(ElementRenderer::ShapeElement *, element);
MidLevelRenderer::MLRShape *mlr_shape = shape->GetMLRShape();
Check_Object(mlr_shape);
for (int p=0; p<mlr_shape->GetNum(); ++p)
{
MidLevelRenderer::MLRPrimitiveBase *primitive = mlr_shape->Find(p);
Check_Object(primitive);
//
//----------------------------------------------------------------
// We have gotten the given primitive, now we need to loop through
// the faces
//----------------------------------------------------------------
//
const Stuff::Point3D *points;
int point_count;
primitive->GetCoordData(&points, &point_count);
MidLevelRenderer::MLRIndexedPrimitiveBase *indexed_primitive =
Cast_Object(
MidLevelRenderer::MLRIndexedPrimitiveBase *,
primitive
);
const unsigned short *indices;
int index_count;
indexed_primitive->GetIndexData(&indices, &index_count);
Verify(!(index_count%3));
int face_count = index_count/3;
Verify(face_count == primitive->GetNumPrimitives());
//
//----------------------------------------------------------------
// We have gotten the given primitive, now we need to loop through
// the faces and get the points of each triangle
//----------------------------------------------------------------
//
int index=0;
for (int f=0; f<face_count; ++f)
{
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point1 = &points[indices[index++]];
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point2 = &points[indices[index++]];
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point3 = &points[indices[index++]];
#if defined(BSP_BUG)
if (Debug_Tile)
{
SPEW((
BSP_BUG,
"Tri %d\tv0=<%f,%f,%f>",
f,
point1->x,
point1->y,
point1->z
));
SPEW((
BSP_BUG,
"\t v1=<%f,%f,%f>",
point2->x,
point2->y,
point2->z
));
SPEW((
BSP_BUG,
"\t v1=<%f,%f,%f>",
point3->x,
point3->y,
point3->z
));
}
#endif
//
//---------------------------------------------
// Figure out the plane equation of the polygon
//---------------------------------------------
//
WORD plane = (*planes_filled)++;
(*planes)[plane].BuildPlane(
*point1,
*point2,
*point3
);
Verify((*planes)[plane].normal.y > Stuff::SMALL);
//
//-----------------------------------------------------------
// Now figure out the plane equation of a vertical plane thru
// each edge
//-----------------------------------------------------------
//
Stuff::Point3D temp = *point1;
temp.y += 10.0f;
(*planes)[(*planes_filled)++].BuildPlane(
*point1,
*point2,
temp
);
(*planes)[(*planes_filled)++].BuildPlane(
*point1,
temp,
*point3
);
temp = *point2;
temp.y += 10.0f;
(*planes)[(*planes_filled)++].BuildPlane(
temp,
*point2,
*point3
);
}
}
}
else
STOP(("Didn't handle this case"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
TestTriangles(
ElementRenderer::Element *element,
Stuff::DynamicArrayOf<Stuff::Plane> &planes,
Stuff::DynamicArrayOf<BYTE> *scoreboard,
int *triangles_tested
)
{
Check_Object(element);
Check_Object(&planes);
Check_Object(scoreboard);
Check_Pointer(triangles_tested);
//
//-----------------------
// Spin through the group
//-----------------------
//
if (element->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData))
{
ElementRenderer::GroupElement *group =
Cast_Object(ElementRenderer::GroupElement *, element);
Stuff::ChainIteratorOf<ElementRenderer::Element*> *children =
group->MakeIterator();
Register_Object(children);
ElementRenderer::Element *child;
while ((child = children->ReadAndNext()) != NULL)
{
Check_Object(child);
TestTriangles(child, planes, scoreboard, triangles_tested);
}
Unregister_Object(children);
delete children;
}
//
//----------------------
// Spin through the list
//----------------------
//
else if (element->IsDerivedFrom(ElementRenderer::ListElement::DefaultData))
{
ElementRenderer::ListElement *list =
Cast_Object(ElementRenderer::ListElement *, element);
for (unsigned i=0; i<list->GetActiveCount(); ++i)
{
ElementRenderer::Element *child = list->GetIndexedElement(i);
Check_Object(child);
TestTriangles(child, planes, scoreboard, triangles_tested);
}
}
//
//-----------------------
// Spin through the shape
//-----------------------
//
else if (element->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData))
{
ElementRenderer::ShapeElement *shape =
Cast_Object(ElementRenderer::ShapeElement *, element);
MidLevelRenderer::MLRShape *mlr_shape = shape->GetMLRShape();
Check_Object(mlr_shape);
for (int p=0; p<mlr_shape->GetNum(); ++p)
{
MidLevelRenderer::MLRPrimitiveBase *primitive = mlr_shape->Find(p);
Check_Object(primitive);
//
//----------------------------------------------------------------
// We have gotten the given primitive, now we need to loop through
// the faces
//----------------------------------------------------------------
//
const Stuff::Point3D *points;
int point_count;
primitive->GetCoordData(&points, &point_count);
MidLevelRenderer::MLRIndexedPrimitiveBase *indexed_primitive =
Cast_Object(
MidLevelRenderer::MLRIndexedPrimitiveBase *,
primitive
);
const unsigned short *indices;
int index_count;
indexed_primitive->GetIndexData(&indices, &index_count);
Verify(!(index_count%3));
int face_count = index_count/3;
Verify(face_count == primitive->GetNumPrimitives());
//
//----------------------------------------------------------------
// We have gotten the given primitive, now we need to loop through
// the faces and get the points of each triangle
//----------------------------------------------------------------
//
int index=0;
int score_index = *triangles_tested * planes.GetLength();
for (int f=0; f<face_count; ++f, ++(*triangles_tested))
{
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point1 = &points[indices[index++]];
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point2 = &points[indices[index++]];
Verify(index < index_count);
Verify(static_cast<unsigned>(indices[index]) < point_count);
const Stuff::Point3D *point3 = &points[indices[index++]];
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((BSP_BUG, "Tri %d\t+", f));
#endif
//
//-----------------------------------------------------
// Now we need to test these points against every plane
//-----------------------------------------------------
//
for (int t=0; t<planes.GetLength(); ++t, ++score_index)
{
Stuff::Scalar dist = planes[t].GetDistanceTo(*point1);
BYTE &entry = (*scoreboard)[score_index];
if (dist > 0.02)
entry = Outside_Plane;
else if (dist < -0.02)
entry = Inside_Plane;
else
entry = On_Plane;
dist = planes[t].GetDistanceTo(*point2);
if (dist > 0.02)
entry |= Outside_Plane;
else if (dist < -0.02)
entry |= Inside_Plane;
else
entry |= On_Plane;
dist = planes[t].GetDistanceTo(*point3);
if (dist > 0.02)
entry |= Outside_Plane;
else if (dist < -0.02)
entry |= Inside_Plane;
else
entry |= On_Plane;
//
//---------------------------------------------------------
// If we are on both sides of the plane, count us as on the
// plane
//---------------------------------------------------------
//
if ((entry&Split_Plane) == Split_Plane)
entry |= On_Plane;
Verify(entry != On_Plane || !(t&3));
#if defined(BSP_BUG)
if (Debug_Tile)
if ((t&3) == 3)
SPEW((BSP_BUG, "%d +", entry));
else
SPEW((BSP_BUG, "%d+", entry));
#endif
}
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((BSP_BUG, ""));
#endif
}
}
}
else
STOP(("Didn't handle this case"));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
float
ComputeScore(
int outsides,
int insides,
int splits,
int total
)
{
//
//-----------------------------------------------------------------------
// The plane is useless if no triangles are reduced on either side of the
// plane, or if this is a real polygon plane and it splits another
// polygon
//-----------------------------------------------------------------------
//
float score;
if (
total == outsides || total == insides
|| (splits>0 && total>outsides+insides-splits)
)
score = 3.0f * total;
//
//---------------------------------------------------------------------
// Otherwise, base the score on the sum of the child triangle count and
// how balanced it is, giving a slight bias towards load over balance
//---------------------------------------------------------------------
//
else
{
float balance = Stuff::Fabs(static_cast<float>(outsides - insides));
score = outsides + insides + 0.9f*balance;
}
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((
BSP_BUG,
"o:%d i:%d s:%d -> %f",
outsides,
insides,
splits,
score
));
#endif
return score;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SortTriangles(
Stuff::DynamicArrayOf<Adept::TerrainBSP> *bsp,
WORD *bsp_count,
Stuff::DynamicArrayOf<Stuff::Plane> *used_planes,
WORD *used_plane_count,
Stuff::DynamicArrayOf<BYTE> &scoreboard,
Stuff::DynamicArrayOf<Stuff::Plane> &reference_planes,
Stuff::DynamicArrayOf<int> &triangles
)
{
Check_Object(bsp);
Check_Pointer(bsp_count);
Check_Object(&scoreboard);
Check_Object(&reference_planes);
Check_Object(&triangles);
//
//---------------------------------------------------------------------
// Our first task is to compile the statistics for each of the possible
// splitting planes
//---------------------------------------------------------------------
//
int plane_count = reference_planes.GetLength();
Stuff::DynamicArrayOf<int>
insides(plane_count),
outsides(plane_count),
splits(plane_count);
unsigned triangle_count = triangles.GetLength();
int p,t;
#if defined(BSP_BUG)
if (Debug_Tile)
{
SPEW((BSP_BUG, "BSP %d:", *bsp_count));
SPEW((BSP_BUG, "\t+"));
for (t=0; t<triangles.GetLength(); ++t)
SPEW((BSP_BUG, "%d +", triangles[t]));
SPEW((BSP_BUG, ""));
}
#endif
for (p=0; p<plane_count; ++p)
{
insides[p] = 0;
outsides[p] = 0;
splits[p] = 0;
//
//--------------------------------------------------------------------
// Test each of the test triangles against the chosen plane and sum up
// the results
//--------------------------------------------------------------------
//
for (t=0; t<triangle_count; ++t)
{
switch (scoreboard[triangles[t]*plane_count + p])
{
case Outside_Plane:
case Outside_Plane|On_Plane:
++outsides[p];
break;
case Inside_Plane:
case Inside_Plane|On_Plane:
++insides[p];
break;
case On_Plane:
Verify(!(p&3));
break;
case Split_Plane|On_Plane:
++outsides[p];
++insides[p];
++splits[p];
break;
default:
STOP(("Shouldn't happen"));
break;
}
}
}
//
//------------------------------------------------------------------------
// Now that we have our results, we need to spin through the table looking
// for the best splitting plane
//------------------------------------------------------------------------
//
int best_plane = -1;
float best_score = 3.0f*triangle_count;
int b_o, b_i, b_s;
for (p=0; p<plane_count; ++p)
{
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((BSP_BUG, "\t\t%d -> +", p));
#endif
float score =
ComputeScore(outsides[p], insides[p], splits[p], triangle_count);
if (score < best_score)
{
best_plane = p;
best_score = score;
b_o = outsides[p];
b_i = insides[p];
b_s = splits[p];
}
}
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((
BSP_BUG,
"\t%d -> O:%d I:%d S:%d = %f ====",
best_plane,
b_o,
b_i,
b_s,
best_score
));
#endif
//
//-----------------------------------------------------
// We couldn't sort the rest so best plane will be zero
//-----------------------------------------------------
//
if (best_plane < 0)
{
best_plane = 0;
WORD new_plane;
for (new_plane=0; new_plane<*used_plane_count; ++new_plane)
{
if (
!memcmp(
&(*used_planes)[new_plane],
&reference_planes[best_plane],
sizeof(Stuff::Plane)
)
)
break;
}
if (new_plane == *used_plane_count)
{
new_plane = (*used_plane_count)++;
(*used_planes)[new_plane] = reference_planes[best_plane];
}
int new_bsp = (*bsp_count)++;
(*bsp)[new_bsp].m_planeIndex = new_plane;
(*bsp)[new_bsp].m_innerIndex = 0;
(*bsp)[new_bsp].m_outerIndex = 0;
return;
}
Verify(best_plane>=0 && best_plane<plane_count);
if (best_score == 3.0f*triangle_count)
STOP(("Can't sort this tree!"));
//
//-------------------------------------------------------
// Search the used plane array to see if the plane exists
//-------------------------------------------------------
//
WORD new_plane;
for (new_plane=0; new_plane<*used_plane_count; ++new_plane)
{
if (
!memcmp(
&(*used_planes)[new_plane],
&reference_planes[best_plane],
sizeof(Stuff::Plane)
)
)
break;
}
if (new_plane == *used_plane_count)
{
new_plane = (*used_plane_count)++;
(*used_planes)[new_plane] = reference_planes[best_plane];
}
//
//-------------------------------------------
// Copy this plane into the used planes array
//-------------------------------------------
//
int new_bsp = (*bsp_count)++;
(*bsp)[new_bsp].m_planeIndex = new_plane;
(*bsp)[new_bsp].m_innerIndex = 0;
(*bsp)[new_bsp].m_outerIndex = 0;
//
//----------------------------------------------------------------
// If all of the triangles are contained in the plane, we are done
//----------------------------------------------------------------
//
if (!insides[best_plane] && !outsides[best_plane])
return;
//
//--------------------------------------
// Split up the triangles into two lists
//--------------------------------------
//
int inside_count = insides[best_plane];
Stuff::DynamicArrayOf<int> inside_triangles(inside_count);
inside_count = 0;
int outside_count = outsides[best_plane];
Stuff::DynamicArrayOf<int> outside_triangles(outside_count);
outside_count = 0;
for (t=0; t<triangles.GetLength(); ++t)
{
BYTE flag = scoreboard[triangles[t]*plane_count + best_plane];
if (flag&Outside_Plane)
outside_triangles[outside_count++] = triangles[t];
if (flag&Inside_Plane)
inside_triangles[inside_count++] = triangles[t];
}
Verify(outside_triangles.GetLength() == outside_count);
Verify(inside_triangles.GetLength() == inside_count);
//
//--------------------------
// Recurse the inside branch
//--------------------------
//
if (inside_count > 0)
{
(*bsp)[new_bsp].m_innerIndex = *bsp_count;
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((BSP_BUG, "\tBSP %d.innerIndex = %d", new_bsp, *bsp_count));
#endif
SortTriangles(
bsp,
bsp_count,
used_planes,
used_plane_count,
scoreboard,
reference_planes,
inside_triangles
);
}
else
{
Verify(outside_count < triangles.GetLength());
Verify(!(best_plane&3));
}
//
//--------------------------
// Recurse the outside branch
//--------------------------
//
if (outside_count > 0)
{
(*bsp)[new_bsp].m_outerIndex = *bsp_count;
#if defined(BSP_BUG)
if (Debug_Tile)
SPEW((BSP_BUG, "\tBSP %d.outerIndex = %d", new_bsp, *bsp_count));
#endif
SortTriangles(
bsp,
bsp_count,
used_planes,
used_plane_count,
scoreboard,
reference_planes,
outside_triangles
);
}
else
{
Verify(inside_count < triangles.GetLength());
Verify(!(best_plane&3));
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MakeBSP(
Stuff::DynamicArrayOf<Stuff::Plane> *planes,
WORD *plane_count,
ElementRenderer::GroupElement *tile,
Stuff::MemoryStream *stream
)
{
Check_Object(tile);
Check_Object(stream);
//
//------------------------------------------------------------------------
// Count the number of triangles in the tile. We will end up computing
// four planes per triangle, one for the face, and one vertical plane thru
// each edge
//------------------------------------------------------------------------
//
int tri_count = tile->CountTriangles();
int ref_plane_count = 4*tri_count;
Stuff::DynamicArrayOf<Stuff::Plane> reference_planes(ref_plane_count);
int planes_filled = 0;
FindPlanes(tile, &reference_planes, &planes_filled);
Verify(planes_filled == ref_plane_count);
//
//-----------------------------------------------------------------------
// We now need to fill the scoreboard up with the poly to plane test data
//-----------------------------------------------------------------------
//
int total_tests = tri_count * ref_plane_count;
Stuff::DynamicArrayOf<BYTE> scoreboard(total_tests);
int triangles_tested = 0;
TestTriangles(tile, reference_planes, &scoreboard, &triangles_tested);
Verify(triangles_tested = tri_count);
//
//------------------------------------------------------------------------
// Now build the triangle index list in order to prime the sorting process
//------------------------------------------------------------------------
//
Stuff::DynamicArrayOf<int> triangles(tri_count);
for (int t=0; t<tri_count; ++t)
triangles[t] = t;
Stuff::DynamicArrayOf<Adept::TerrainBSP> bsp(ref_plane_count);
WORD bsp_count = 0;
//
//---------------
// Sort the puppy
//---------------
//
SortTriangles(
&bsp,
&bsp_count,
planes,
plane_count,
scoreboard,
reference_planes,
triangles
);
Verify(bsp_count < bsp.GetLength());
//
//-------------------
// Write out the data
//-------------------
//
*stream << bsp_count;
stream->WriteBytes(
bsp.GetData(),
bsp_count*sizeof(Adept::TerrainBSP)
);
}