Files
firestorm/Gameleap/code/mw4/Libraries/Proxies/InterestBSP.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

1409 lines
41 KiB
C++

#include "ProxyHeaders.hpp"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SortInterestZonesProcess::SortInterestZonesProcess(
Stuff::NotationFile *config_file,
const char* set_path,
const Stuff::DynamicArrayOf<Stuff::ExtentBox> &zone_array,
BinSortProcess *bin_process,
FindErrorsProcess *find_errors
):
Process(config_file),
zoneArray(zone_array)
{
Check_Object(config_file);
Check_Pointer(set_path);
Check_Object(&zone_array);
configFile = config_file;
setDirectory = set_path;
zoneCounter = 0;
setContents = NULL;
trimmingTolerance = 1e-2f;
Page *page = config_file->FindPage("InterestBSP");
if (page)
page->GetEntry("TrimmingTolerance", &trimmingTolerance);
sortProcess = bin_process;
errorProcess = find_errors;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SortInterestZonesProcess::SplitCallback(GenericProxy *proxy)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
SortInterestZonesProcess::CreateCallback(GenericProxy *proxy)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
FlattenHierarchyProcess*
SortInterestZonesProcess::MakeFlattenHierarchyProcess(
Stuff::NotationFile *config_file,
GroupProxy *parent_group
)
{
Check_Object(this);
return new FlattenHierarchyProcess(config_file, parent_group);
}
//
//############################################################################
//########################### InterestBSP ##############################
//############################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InterestBSP::~InterestBSP()
{
if (insideNode)
{
Check_Object(insideNode);
delete insideNode;
}
if (outsideNode)
{
Check_Object(outsideNode);
delete outsideNode;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InterestBSP*
InterestBSP::CreateBSPTree(SortInterestZonesProcess *process)
{
Check_Object(process);
//
//-----------------------------------
// Create the candidate set of planes
//-----------------------------------
//
DynamicArrayOf<Plane> planes;
Find_Planes_Of_Boxes(&planes, process->zoneArray);
//
//------------------------------------------------------------
// Create the scoreboard, and test each box against each plane
//------------------------------------------------------------
//
unsigned plane_count = planes.GetLength();
unsigned zone_count = process->zoneArray.GetLength();
DynamicArrayOf<int> scoreboard(plane_count * zone_count);
int *entry = &scoreboard[0];
for (unsigned plane=0; plane<plane_count; ++plane)
{
for (unsigned zone=0; zone<zone_count; ++zone, ++entry)
{
Verify(
static_cast<unsigned>(entry - &scoreboard[0])
< plane_count * zone_count
);
if (
planes[plane].ContainsAllOf(
process->zoneArray[zone],
process->planeThicknessTolerance
)
)
*entry = ContainsAll;
else if (
planes[plane].ContainsSomeOf(
process->zoneArray[zone],
process->planeThicknessTolerance
)
)
*entry = Splits;
else
*entry = ContainsNone;
}
}
//
//-----------------------------------------
// Create an index into the zones structure
//-----------------------------------------
//
DynamicArrayOf<unsigned> index(zone_count);
for (unsigned zone=0; zone<zone_count; ++zone)
index[zone] = zone;
//
//------------------------------------------------
// Start the BSP process, returning the first node
//------------------------------------------------
//
InterestBSP *tree =
MakeBSPNode(process, scoreboard, planes, index, zone_count);
Check_Object(tree);
return tree;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
InterestBSP*
InterestBSP::MakeBSPNode(
SortInterestZonesProcess *process,
const Stuff::DynamicArrayOf<int> &scoreboard,
const Stuff::DynamicArrayOf<Stuff::Plane> &planes,
Stuff::DynamicArrayOf<unsigned> &index,
unsigned zone_count
)
{
Check_Object(&scoreboard);
Check_Object(&planes);
unsigned plane_count = planes.GetLength();
Verify(plane_count > 0);
Verify(zone_count > 0);
Verify(plane_count * zone_count == scoreboard.GetLength());
Check_Object(&index);
unsigned active_count = index.GetLength();
Verify(active_count <= zone_count);
Verify(active_count > 0);
//
//---------------------------------------------------------------------
// If the active zone list only contains one zone then we create a leaf
// BSP node that holds it
//---------------------------------------------------------------------
//
InterestBSP *node = new InterestBSP;
if (active_count == 1)
{
node->zoneIndex = index[0];
return node;
}
node->zoneIndex = zone_count;
//
//-------------------------------------------------------------------------
// Our first step is to build a summary of the scoreboard information on
// all the active zones across all the separation planes. The summary will
// be based upon the balance achieved by that particular plane on the
// current set of zones
//-------------------------------------------------------------------------
//
DynamicArrayOf<int> summary(plane_count);
unsigned plane;
for (plane=0; plane<plane_count; ++plane)
{
const int *row = &scoreboard[plane * zone_count];
//
//----------------------------------------------------------------------
// Spin through each box, looking for the balance information. Boxes on
// the inside of the plane will be positive, boxes on the outside will
// be negative, and splits will abort the summary and set the plane as
// totally unbalanced
//----------------------------------------------------------------------
//
summary[plane] = 0;
for (unsigned i=0; i<active_count; ++i)
{
unsigned zone = index[i];
Verify(zone < zone_count);
if (row[zone] == ContainsAll)
++summary[plane];
else if (row[zone] == ContainsNone)
--summary[plane];
else
{
summary[plane] = active_count;
break;
}
}
}
//
//-----------------------------------------
// Now we must find the best balanced plane
//-----------------------------------------
//
unsigned best_plane = plane_count;
int best_balance = active_count;
for (plane=0; plane<plane_count; ++plane)
{
if (Abs(summary[plane]) < Abs(best_balance))
{
best_balance = summary[plane];
best_plane = plane;
}
}
if (best_plane == plane_count)
STOP(("Couldn't find splitting plane!"));
//
//------------------------------------------------------------
// Figure out the sizes of the inside and outside index arrays
//------------------------------------------------------------
//
node->dividingPlane = planes[best_plane];
Verify(!((active_count + best_balance)&1));
unsigned in_size = (active_count + best_balance) >> 1;
unsigned out_size = active_count - in_size;
DynamicArrayOf<unsigned> in(in_size);
DynamicArrayOf<unsigned> out(out_size);
//
//----------------------------------------------------------------------
// Run through the list, moving the index values to the appropriate list
//----------------------------------------------------------------------
//
unsigned in_count = 0;
unsigned out_count = 0;
const int *row = &scoreboard[best_plane * zone_count];
for (unsigned i=0; i<active_count; ++i)
{
unsigned zone = index[i];
Verify(zone < zone_count);
if (row[zone] == ContainsAll)
in[in_count++] = zone;
else
{
Verify(row[zone] == ContainsNone);
out[out_count++] = zone;
}
}
Verify(in_count - out_count == best_balance);
//
//-------------------------------------
// Run the BSP process on each sub-list
//-------------------------------------
//
node->insideNode =
MakeBSPNode(process, scoreboard, planes, in, zone_count);
Check_Object(node->insideNode);
node->outsideNode =
MakeBSPNode(process, scoreboard, planes, out, zone_count);
Check_Object(node->outsideNode);
return node;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
InterestBSP::SplitScene(
SortInterestZonesProcess *process,
SceneProxy *scene
)
{
Check_Object(this);
Check_Object(process);
Check_Object(scene);
//
//---------------------------------------
// Make sure that the process says its OK
//---------------------------------------
//
process->SplitCallback(scene);
if (!process->continueProcess)
return;
//
//----------------------------------------------------------------------
// We are not a leaf node, so find out how many children we need to test
// against the BSP plane. If there are none, just return
//----------------------------------------------------------------------
//
unsigned child_count = scene->GetChildCount();
if (!child_count)
return;
//
//--------------------------------------------------------------------
// If there are no children of this BSP node, we need to write out the
// interest zone info
//--------------------------------------------------------------------
//
if (!insideNode && !outsideNode)
{
CreateInterestZone(process, scene);
return;
}
//
//------------------------------------------------------------------------
// Create the two new group nodes that the meshes will be sorted into,
// then run through the original children and sort each one into the
// appropriate set
//------------------------------------------------------------------------
//
GroupProxy *inside_set = scene->AppendNewGroupProxy();
Check_Object(inside_set);
GroupProxy *outside_set = scene->AppendNewGroupProxy();
Check_Object(outside_set);
ChildProxy *child = scene->UseFirstChildProxy();
for (unsigned i=0; i<child_count; ++i)
{
Check_Object(child);
ChildProxy *next = child->UseNextSiblingProxy();
if (
SplitMesh(
process,
Cast_Object(PolygonMeshProxy*, child),
inside_set,
outside_set
)
)
child->DetachReference();
child = next;
if (!process->continueProcess)
break;
}
if (child)
child->DetachReference();
if (!process->continueProcess)
{
inside_set->DetachReference();
outside_set->DetachReference();
return;
}
//
//-----------------------------------------------------------
// Now, split each group against the next lower BSP nodes
//-----------------------------------------------------------
//
Check_Object(insideNode);
if (insideNode->SplitGroup(process, inside_set))
inside_set->DetachReference();
if (!process->continueProcess)
outside_set->DetachReference();
else
{
Check_Object(outsideNode);
if (outsideNode->SplitGroup(process, outside_set))
outside_set->DetachReference();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
InterestBSP::SplitGroup(
SortInterestZonesProcess *process,
GroupProxy *group
)
{
Check_Object(this);
Check_Object(process);
Check_Object(group);
//
//---------------------------------------
// Make sure that the process says its OK
//---------------------------------------
//
process->SplitCallback(group);
if (!process->continueProcess)
return true;
//
//----------------------------------------------------------------------
// We are not a leaf node, so find out how many children we need to test
// against the BSP plane. If there are none, just return
//----------------------------------------------------------------------
//
unsigned child_count = group->GetChildCount();
if (!child_count)
{
group->Destroy();
return false;
}
//
//--------------------------------------------------------------------
// If there are no children of this BSP node, we need to write out the
// interest zone info
//--------------------------------------------------------------------
//
if (!insideNode && !outsideNode)
return CreateInterestZone(process, group);
//
//------------------------------------------------------------------------
// Create the two new group nodes that the meshes will be sorted into,
// then run through the original children and sort each one into the
// appropriate set
//------------------------------------------------------------------------
//
GroupProxy *inside_set = group->AppendNewGroupProxy();
Check_Object(inside_set);
GroupProxy *outside_set = group->AppendNewGroupProxy();
Check_Object(outside_set);
ChildProxy *child = group->UseFirstChildProxy();
for (unsigned i=0; i<child_count; ++i)
{
Check_Object(child);
ChildProxy *next = child->UseNextSiblingProxy();
if (
SplitMesh(
process,
Cast_Object(PolygonMeshProxy*, child),
inside_set,
outside_set
)
)
child->DetachReference();
child = next;
if (!process->continueProcess)
break;
}
if (child)
child->DetachReference();
if (!process->continueProcess)
{
inside_set->DetachReference();
outside_set->DetachReference();
return true;
}
//
//-----------------------------------------------------------
// Now, split each group against the next lower BSP nodes
//-----------------------------------------------------------
//
Check_Object(insideNode);
if (insideNode->SplitGroup(process, inside_set))
inside_set->DetachReference();
if (!process->continueProcess)
outside_set->DetachReference();
else
{
Check_Object(outsideNode);
if (outsideNode->SplitGroup(process, outside_set))
outside_set->DetachReference();
}
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
static inline int
Find_Side(
Scalar distance,
Scalar thickness
)
{
if (distance > thickness)
return 1;
else if (distance < -thickness)
return -1;
return 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
InterestBSP::SplitMesh(
SortInterestZonesProcess *process,
PolygonMeshProxy *mesh,
GroupProxy *inside_set,
GroupProxy *outside_set
)
{
Check_Object(this);
Check_Object(mesh);
Check_Object(inside_set);
Check_Object(outside_set);
//
//---------------------------------------
// Make sure that the process says its OK
//---------------------------------------
//
process->SplitCallback(mesh);
if (!process->continueProcess)
return true;
//
//-------------------------------------------------------------
// Rotate the separating plane into the local space of the mesh
//-------------------------------------------------------------
//
LinearMatrix4D local_to_world;
mesh->GetLocalToParent(&local_to_world);
LinearMatrix4D world_to_local;
world_to_local.Invert(local_to_world);
Plane local_plane;
local_plane.Multiply(dividingPlane, world_to_local);
//
//------------------------------------------------------------------------
// The first task is to find out which side of the plane the mesh is to
// be on. We do this by looking at all the vertices and categorizing each
// one
//------------------------------------------------------------------------
//
int type = 0;
Scalar thickness = process->trimmingTolerance;
DynamicArrayOf<VertexProxy*> vertices;
unsigned vertex_count = mesh->UseVertexArray(&vertices);
unsigned i;
VertexProxy *vertex;
for (i=0; i<vertex_count; ++i)
{
vertex = vertices[i];
Check_Object(vertex);
Point3D position;
vertex->GetPosition(&position);
Scalar distance = local_plane.GetDistanceTo(position);
if (distance < -thickness)
type |= 1;
else if (distance > thickness)
type |= 2;
if (type == 3)
break;
}
mesh->DetachArrayReferences(&vertices);
Verify(mesh->GetReferenceCount() == 1);
//
//-------------------------------------------------------------------------
// Put the mesh in the correct bin based upon the index status. Make sure
// to put empty or coplanar meshes on the inside mesh
//-------------------------------------------------------------------------
//
if (type<2)
{
mesh->TransferAndAppendToParentGroup(inside_set);
return true;
}
//
//-----------------------------------------------------------------------
// Case 2 means that the mesh fits totally outside the plane's half-space
//-----------------------------------------------------------------------
//
else if (type==2)
{
mesh->TransferAndAppendToParentGroup(outside_set);
return true;
}
//
//-------------------------------------------------------------------------
// If we get here, then we have to split the polygon mesh apart, so get the
// list of polygons
//-------------------------------------------------------------------------
//
Verify(type == 3);
DynamicArrayOf<PolygonProxy*> old_polygons;
unsigned old_polygon_count = mesh->UsePolygonArray(&old_polygons);
//
//---------------------------
// Create the polygon buffers
//---------------------------
//
DynamicArrayOf<PolygonProxy*> inside_polygons(old_polygon_count);
unsigned inside_polygon_count = 0;
DynamicArrayOf<PolygonProxy*> outside_polygons(old_polygon_count);
unsigned outside_polygon_count = 0;
DynamicArrayOf<VertexProxy*> new_vertices(2*old_polygon_count);
unsigned new_vertex_count = 0;
//
//-----------------------------------------------------------------------
// Spin through the polygons, looking at each polygon to see if it can be
// transferred as is to one side or the other
//-----------------------------------------------------------------------
//
for (i=0; i<old_polygon_count; ++i)
{
PolygonProxy *polygon = old_polygons[i];
Check_Object(polygon);
type = 0;
DynamicArrayOf<IndexProxy*> old_indices;
unsigned old_index_count = polygon->UseIndexArray(&old_indices);
Verify(old_index_count >= 3);
DynamicArrayOf<Scalar> distances(old_index_count);
unsigned j;
for (j=0; j<old_index_count; ++j)
{
Check_Object(old_indices[j]);
vertex = old_indices[j]->GetVertexProxy();
Check_Object(vertex);
Point3D position;
vertex->GetPosition(&position);
distances[j] = local_plane.GetDistanceTo(position);
if (distances[j] < -thickness)
type |= 1;
else if (distances[j] > thickness)
type |= 2;
}
//
//-----------------------------------------------------------------
// If the polygon is co-planar or inside, put it on the inside set
//-----------------------------------------------------------------
//
if (type<2)
{
polygon->AttachReference();
inside_polygons[inside_polygon_count++] = polygon;
polygon->DetachArrayReferences(&old_indices);
continue;
}
//
//-----------------------------------------------------
// If the polygon is outside, put it on the outside set
//-----------------------------------------------------
//
if (type==2)
{
polygon->AttachReference();
outside_polygons[outside_polygon_count++] = polygon;
polygon->DetachArrayReferences(&old_indices);
continue;
}
//
//----------------------------------------------------------------------
// We have to split this polygon, which will basically mean that we will
// build two new polygons, one per side
//----------------------------------------------------------------------
//
Verify(type==3);
DynamicArrayOf<VertexProxy*> inside_indices(old_index_count+1);
unsigned inside_index_count = 0;
DynamicArrayOf<VertexProxy*> outside_indices(old_index_count+1);
unsigned outside_index_count = 0;
//
//---------------------------
// Set up the trailing vertex
//---------------------------
//
Scalar distance_a = distances[old_index_count-1];
int side_a = Find_Side(distance_a, thickness);
//
//--------------------------
// Set up the testing vertex
//--------------------------
//
Scalar distance_b = distances[0];
int side_b = Find_Side(distance_b, thickness);
Check_Object(old_indices[0]);
VertexProxy *vertex_b = old_indices[0]->GetVertexProxy();
Check_Object(vertex_b);
Point3D position_b;
vertex_b->GetPosition(&position_b);
//
//--------------------------
// Set up the leading vertex
//--------------------------
//
unsigned lead_index = 1;
Scalar distance_c = distances[lead_index];
int side_c = Find_Side(distance_c, thickness);
Check_Object(old_indices[lead_index]);
VertexProxy *vertex_c = old_indices[lead_index]->GetVertexProxy();
Check_Object(vertex_c);
Point3D position_c;
vertex_c->GetPosition(&position_c);
//
//-------------------------
// Loop through the indices
//-------------------------
//
Scalar welding = process->duplicateVertexTolerance;
int crossings = 0;
for (j=0; j<old_index_count; ++j)
{
if (j)
{
side_b = side_c;
vertex_b = vertex_c;
position_b = position_c;
distance_b = distance_c;
if (++lead_index == old_index_count)
lead_index = 0;
Check_Object(old_indices[lead_index]);
vertex_c = old_indices[lead_index]->GetVertexProxy();
Check_Object(vertex_c);
vertex_c->GetPosition(&position_c);
distance_c = distances[lead_index];
side_c = Find_Side(distance_c, thickness);
}
//
//-------------------------------------------------------------------
// Assign this vertex to either or both the inside or outside polygon
// (it can go to both if it is on the plane
//-------------------------------------------------------------------
//
if (side_b < 1)
inside_indices[inside_index_count++] = vertex_b;
if (side_b > -1)
outside_indices[outside_index_count++] = vertex_b;
//
//------------------------------------------------
// Check to see if this index is a splitting index
//------------------------------------------------
//
Analyze_Vertex:
if (!side_b && side_a && side_a == -side_c)
{
++crossings;
Verify(crossings < 3);
side_a = side_b;
}
//
//----------------------------------------------------------------
// Check to see if the edge from b to c needs to be split. If so,
// calculate where the crossing would happen
//----------------------------------------------------------------
//
else if (side_b && side_b == -side_c)
{
Scalar lerp = distance_b / (distance_b - distance_c);
Point3D position;
position.Lerp(position_b, position_c, lerp);
//
//---------------------------------------------------------------
// Before we actually do the split, we need to make sure that new
// vertex doesn't effectively duplicate either of the existing
// vertices
//---------------------------------------------------------------
//
if (
Small_Enough(lerp)
|| Close_Enough(position, position_b, welding)
)
{
if (side_b > 0)
inside_indices[inside_index_count++] = vertex_b;
else
outside_indices[outside_index_count++] = vertex_b;
side_b = 0;
distance_b = 0.0f;
goto Analyze_Vertex;
}
else if (
Close_Enough(lerp, 1.0f)
|| Close_Enough(position, position_c, welding)
)
{
side_c = 0;
distance_c = 0.0f;
goto Analyze_Vertex;
}
//
//----------------------
// Create the new vertex
//----------------------
//
vertex = VertexProxy::MakeProxy();
Check_Object(vertex);
new_vertices[new_vertex_count++] = vertex;
vertex->SetPosition(position);
//
//---------------
// Lerp the color
//---------------
//
RGBAColor color_b;
if (vertex_b->GetColor(&color_b))
{
RGBAColor color_c;
#if defined(_ARMOR)
bool result =
#endif
vertex_c->GetColor(&color_c);
Verify(result);
RGBAColor color;
color.Lerp(color_b, color_c, lerp);
vertex->SetColor(color);
}
//
//---------------
// Lerp the color
//---------------
//
DynamicArrayOf<Vector2DOf<Scalar> > uv_b;
if (vertex_b->GetUVs(&uv_b))
{
DynamicArrayOf<Vector2DOf<Scalar> > uv_c;
#if defined(_ARMOR)
bool result =
#endif
vertex_c->GetUVs(&uv_c);
Verify(result);
Verify(uv_b.GetLength() == uv_c.GetLength());
DynamicArrayOf<Vector2DOf<Scalar> > uv;
uv.SetLength(uv_b.GetLength());
for(unsigned uv_loop=0;uv_loop<uv_b.GetLength();uv_loop++)
{
uv[uv_loop].Lerp(uv_b[uv_loop], uv_c[uv_loop], lerp);
}
vertex->SetUVs(uv);
}
//
//---------------
// Lerp the color
//---------------
//
Normal3D normal_b;
if (vertex_b->GetNormal(&normal_b))
{
Normal3D normal_c;
#if defined(_ARMOR)
bool result =
#endif
vertex_c->GetNormal(&normal_c);
Verify(result);
Normal3D normal;
normal.Lerp(normal_b, normal_c, lerp);
vertex->SetNormal(normal);
}
//
//---------------------------------------
// Now, insert the vertex into both sides
//---------------------------------------
//
++crossings;
Verify(crossings < 3);
inside_indices[inside_index_count++] = vertex;
outside_indices[outside_index_count++] = vertex;
side_a = 0;
}
}
//
//----------------------------------------------------------
// See if we actually ended up with a polygon on the outside
//----------------------------------------------------------
//
if (inside_index_count < 3)
{
Verify(outside_index_count >= 3);
Move_It_Outside:
polygon->AttachReference();
outside_polygons[outside_polygon_count++] = polygon;
}
//
//---------------------------------------------------------
// See if we actually ended up with a polygon on the inside
//---------------------------------------------------------
//
else if (outside_index_count < 3)
{
Verify(inside_index_count >= 3);
Move_It_Inside:
polygon->AttachReference();
inside_polygons[inside_polygon_count++] = polygon;
}
//
//------------------------------------------
// Construct the inside and outside polygons
//------------------------------------------
//
else
{
//
//---------------------------------------------
// Make sure that the outside polygon isn't bad
//---------------------------------------------
//
Verify(outside_index_count >= 3);
outside_indices.SetLength(outside_index_count);
PolygonProxy *outside_polygon = PolygonProxy::MakeProxy();
Check_Object(outside_polygon);
outside_polygon->SetPolygonIndices(outside_indices);
if (outside_polygon->FindErrors(process->errorProcess) > 0)
{
outside_polygon->DetachReference();
goto Move_It_Inside;
}
//
//--------------------------------------------
// Make sure that the inside polygon isn't bad
//--------------------------------------------
//
Verify(inside_index_count >= 3);
inside_indices.SetLength(inside_index_count);
PolygonProxy *inside_polygon = PolygonProxy::MakeProxy();
Check_Object(inside_polygon);
inside_polygon->SetPolygonIndices(inside_indices);
if (inside_polygon->FindErrors(process->errorProcess) > 0)
{
outside_polygon->DetachReference();
inside_polygon->DetachReference();
goto Move_It_Outside;
}
//
//---------------------------------------------
// This is good, so actually setup both proxies
//---------------------------------------------
//
MultiState multi_state;
polygon->UseMultiState(&multi_state);
Check_Object(&multi_state);
outside_polygon->SetStatesToMatch(multi_state);
inside_polygon->SetStatesToMatch(multi_state);
multi_state.DetachReferences();
outside_polygons[outside_polygon_count++] = outside_polygon;
inside_polygons[inside_polygon_count++] = inside_polygon;
}
//
//---------------------
// Clean up the polygon
//---------------------
//
polygon->DetachArrayReferences(&old_indices);
}
//
//--------------------------------------------------
// See if we should just transfer the mesh after all
//--------------------------------------------------
//
bool destroy_mesh;
if (!inside_polygon_count)
{
Verify(outside_polygon_count == old_polygon_count);
mesh->TransferAndAppendToParentGroup(outside_set);
destroy_mesh = false;
}
else if (!outside_polygon_count)
{
Verify(inside_polygon_count == old_polygon_count);
mesh->TransferAndAppendToParentGroup(inside_set);
destroy_mesh = false;
}
//
//------------------------------------------------------
// Otherwise, create the new meshes and add the polygons
//------------------------------------------------------
//
else
{
PolygonMeshProxy *inside_mesh = inside_set->AppendNewPolygonMeshProxy();
Check_Object(inside_mesh);
inside_polygons.SetLength(inside_polygon_count);
inside_mesh->AddPolygons(process, inside_polygons);
inside_mesh->DetachReference();
PolygonMeshProxy *outside_mesh =
outside_set->AppendNewPolygonMeshProxy();
Check_Object(outside_mesh);
outside_polygons.SetLength(outside_polygon_count);
outside_mesh->AddPolygons(process, outside_polygons);
outside_mesh->DetachReference();
destroy_mesh = true;
}
//
//------------------------------------------------------------------------
// Clean up the buffer arrays. Passing NULL into the polygon detach array
// function cleans up the abstract indices
//------------------------------------------------------------------------
//
for (i=0; i<inside_polygon_count; ++i)
inside_polygons[i]->DetachReference();
for (i=0; i<outside_polygon_count; ++i)
outside_polygons[i]->DetachReference();
for (i=0; i<new_vertex_count; ++i)
new_vertices[i]->DetachReference();
//
//------------------------------------
// This mesh may be no longer required
//------------------------------------
//
mesh->DetachArrayReferences(&old_polygons);
Verify(mesh->GetReferenceCount() == 1);
if (destroy_mesh)
mesh->Destroy();
return !destroy_mesh;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
InterestBSP::CreateInterestZone(
SortInterestZonesProcess *process,
GenericProxy *proxy
)
{
Check_Object(this);
Check_Object(process);
Check_Object(proxy);
//
//---------------------------------------
// Make sure that the process says its OK
//---------------------------------------
//
process->CreateCallback(proxy);
if (!process->continueProcess)
return true;
//
//----------------------------------------------------------------------
// Create the node name for this interest zone, and assign it as the new
// proxy name
//----------------------------------------------------------------------
//
MString zone_name = "iz";
char buffer[200];
_itoa(process->zoneCounter++, buffer, 10);
zone_name += buffer;
//
//-------------------------------------------------------------------------
// Get the centerpoint of the box, and then convert that to a linear
// transform we can apply to the child meshes in order to get them centered
// around the world origin
//-------------------------------------------------------------------------
//
Check_Object(&process->zoneArray);
const ExtentBox *box = &process->zoneArray[zoneIndex];
Check_Object(box);
Point3D box_center;
box->GetCenterpoint(&box_center);
LinearMatrix4D box_to_world(box_center);
LinearMatrix4D world_to_box;
world_to_box.Invert(box_to_world);
//
//----------------------------------------------------------------------
// We need to see if we are working on a scene or group, and get the
// proper child info from either
//----------------------------------------------------------------------
//
unsigned child_count;
GroupProxy *basket;
ChildProxy *child;
GroupProxy *iz;
if (proxy->IsDerivedFrom(GroupProxy::DefaultData))
{
GroupProxy *group = Cast_Object(GroupProxy*, proxy);
iz = group;
child_count = group->GetChildCount();
basket = group->AppendNewGroupProxy();
child = group->UseFirstChildProxy();
iz->AttachReference();
}
else
{
Verify(proxy->IsDerivedFrom(SceneProxy::DefaultData));
SceneProxy *scene = Cast_Object(SceneProxy*, proxy);
child_count = scene->GetChildCount();
iz = scene->AppendNewGroupProxy();
basket = iz->AppendNewGroupProxy();
child = scene->UseFirstChildProxy();
}
//
//--------------------------------------------
// Name the basket and move each child into it
//--------------------------------------------
//
MString set_name = "floor";
set_name += buffer;
iz->SetName(zone_name);
basket->SetName(set_name);
for (unsigned i=0; i<child_count; ++i)
{
ChildProxy *next = child->UseNextSiblingProxy();
child->TransformLocalToParent(world_to_box);
child->TransferAndAppendToParentGroup(basket);
child->DetachReference();
child = next;
}
if (child)
child->DetachReference();
//
//------------------------------------------------------------------------
// Optimize the basket, and move it in the hierarchy to where it should be
// viewed at
//------------------------------------------------------------------------
//
OptimizeBasket(process, basket);
basket->SetLocalToParent(box_to_world);
basket->DetachReference();
//
//------------------------------------------------------------------------
// Now we need to generate the contents file for this interest zone. It
// will specify a single entity which will try and hook up to the izset###
// locator.
//------------------------------------------------------------------------
//
Check_Pointer(process->setDirectory);
MString contents_name = process->setDirectory;
contents_name += zone_name;
contents_name += ".contents";
NotationFile contents_file;
const char* page_name = "Floor";
Page *page = contents_file.SetPage(page_name);
Check_Object(page);
page->SetEntry("Model", "Floor.data");
char center_text[200];
sprintf(center_text, "%f %f %f", box_center.x, box_center.y, box_center.z);
page->SetEntry("Translation", center_text);
page->SetEntry("PreCollisionState", "NeverExecuteState");
page->SetEntry("PostCollisionState", "NeverExecuteState");
page->SetEntry("CollisionMask", "-1");
page->SetEntry("Collidee", "yes");
page->SetEntry("ParentedToInterestGraphFlag", "yes");
page->SetEntry("CanInterestFlag", "yes");
page->SetEntry("CulledInterestFlag", "yes");
MString locator_name = "\"";
locator_name += set_name;
locator_name += '"';
page->SetEntry("Locator", locator_name);
contents_file.SaveAs(contents_name);
//
//-----------------------------------------------------------------------
// Create the interest zone entry within the set contents file. The page
// name will be InterestZone###
//-----------------------------------------------------------------------
//
Check_Object(process->setContents);
NotationFile *set_contents_file = process->setContents;
page_name = zone_name;
Page *set_page = set_contents_file->SetPage(page_name);
Check_Object(set_page);
set_page->SetEntry("Model", "InterestZone.data");
set_page->SetEntry("PreCollisionState", "NeverExecuteState");
set_page->SetEntry("PostCollisionState", "NeverExecuteState");
set_page->SetEntry("ParentedToInterestGraphFlag", "no");
set_page->SetEntry("CanInterestFlag", "yes");
set_page->SetEntry("CulledInterestFlag", "no");
//
//------------------------------------------------------------------------
// Set up the locator the interest zone is to search for, put the box size
// in ASCII, and finish the page
//------------------------------------------------------------------------
//
locator_name = "\"";
locator_name += zone_name;
locator_name += '"';
set_page->SetEntry("Locator", locator_name);
set_page->SetEntry("ArmoryZoneFlag", "no");
sprintf(
buffer,
"%f,%f,%f,%f,%f,%f",
box->minX,
box->minY,
box->minZ,
box->maxX,
box->maxY,
box->maxZ
);
set_page->SetEntry("Box", buffer);
set_page->SetEntry("Contents", zone_name + ".contents");
//
//----------------------------------------------------
// Hierarchy proxies must be discarded prior to return
//----------------------------------------------------
//
iz->DetachReference();
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
InterestBSP::OptimizeBasket(
SortInterestZonesProcess *process,
GroupProxy *basket
)
{
Check_Object(this);
Check_Object(process);
Check_Object(basket);
//
//---------------------------
// Flatten out the transforms
//---------------------------
//
FlattenHierarchyProcess *flatten_hierarchy =
process->MakeFlattenHierarchyProcess(process->configFile, basket);
Check_Object(flatten_hierarchy);
basket->FlattenHierarchy(flatten_hierarchy);
Check_Object(flatten_hierarchy);
delete flatten_hierarchy;
//
//--------------------------------------------------------------------------
// Now that all the children are split up by state, the next step is to fuse
// polygon mesh children that have the same state
//--------------------------------------------------------------------------
//
unsigned child_count = basket->GetChildCount();
if (!child_count)
return;
//
//---------------------------
// Set up the analysis arrays
//---------------------------
//
DynamicArrayOf<PolygonMeshProxy*> meshes(child_count);
DynamicArrayOf<MultiState*> states(child_count);
DynamicArrayOf<DynamicArrayOf<PolygonProxy*> >
polygon_arrays(child_count);
unsigned unique_states = 0;
//
//-----------------------------------------------------------------
// Loop through each mesh looking for the proper state bin for each
//-----------------------------------------------------------------
//
ChildProxy *child = basket->UseFirstChildProxy();
unsigned
m,
s;
for (m=0; m<child_count; ++m)
{
Check_Object(child);
ChildProxy *next = child->UseNextSiblingProxy();
meshes[m] = Cast_Object(PolygonMeshProxy*, child);
#if defined(_ARMOR)
LinearMatrix4D test_matrix;
Verify(!meshes[m]->GetLocalToParent(&test_matrix));
#endif
//
//-------------------
// Find a state proxy
//-------------------
//
DynamicArrayOf<PolygonProxy *> polygons;
unsigned poly_count = meshes[m]->UsePolygonArray(&polygons);
MultiState poly_states;
polygons[0]->UseMultiState(&poly_states);
//
//-------------------------------------------------------
// See if this state matches any of our previous children
//-------------------------------------------------------
//
for (s=0; s<unique_states;++s)
{
if(poly_states.IsEqualTo(*states[s]))
{
break;
}
}
//
//----------------------------
// Keep the state if it is new
//----------------------------
//
if (s<unique_states)
{
poly_states.DetachReferences();
}
else
{
states[s] = new MultiState(poly_states);
unique_states++;
}
//
//--------------------------------------------------
// Copy the polygon proxies into the appropriate bin
//--------------------------------------------------
//
unsigned i = polygon_arrays[s].GetLength();
polygon_arrays[s].SetLength(i + poly_count);
for (unsigned j=0; j<poly_count; ++j)
polygon_arrays[s][i+j] = polygons[j];
//
//-----------------------
// Move to the next child
//-----------------------
//
child = next;
}
Verify(!child);
Verify(unique_states>0);
//
//------------------------------------------------------------------------
// Bucket sort each state separately. The state proxy was only needed for
// the sorting, so we don't need it anymore
//------------------------------------------------------------------------
//
for (s=0; s<unique_states;++s)
{
states[s]->DetachReferences();
basket->SortAndAddPolygons(
process->sortProcess,
polygon_arrays[s]
);
meshes[s]->DetachArrayReferences(&polygon_arrays[s]);
}
//
//----------------------
// Remove the old meshes
//----------------------
//
for (m=0; m<child_count; ++m)
{
Check_Object(meshes[m]);
Verify(meshes[m]->GetReferenceCount() == 1);
meshes[m]->Destroy();
}
//
//------------------------------------------------------------------------
// Now binsort everything. Since the meshes are already within the bucket
// size, this will just create new groups to arrange them within
//------------------------------------------------------------------------
//
basket->BinSort(process->sortProcess);
}