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.
1409 lines
41 KiB
C++
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);
|
|
}
|