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

616 lines
16 KiB
C++

#include "ProxyHeaders.hpp"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
FindErrorsProcess::FindErrorsProcess():
enableDuplicateCheck(true),
enableDegenerateCheck(true),
enableCoplanarCheck(true),
enableColinearCheck(true),
enableConvexCheck(true)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
FindErrorsProcess::FindErrorsProcess(
Stuff::NotationFile *data_file,
bool bSuppress,
void* fcn
):
Process(data_file,bSuppress,fcn),
enableDuplicateCheck(true),
enableDegenerateCheck(true),
enableCoplanarCheck(true),
enableColinearCheck(true),
enableConvexCheck(true)
{
Check_Object(data_file);
Page *page = data_file->FindPage("FindErrors");
if (page)
{
page->GetEntry("DuplicateCheck", &enableDuplicateCheck);
page->GetEntry("DegenerateCheck", &enableDegenerateCheck);
page->GetEntry("CoplanarCheck", &enableCoplanarCheck);
page->GetEntry("ColinearCheck", &enableColinearCheck);
page->GetEntry("ConvexCheck", &enableConvexCheck);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
PolygonProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//---------------------------------------------------------------------
// Get the area of the polygon. This will be used to scale some of the
// error process so they fit with larger polygons
//---------------------------------------------------------------------
//
Scalar area = GetArea();
Scalar area_edge = Sqrt(area);
Scalar plane_tolerance = process->planeThicknessTolerance * area_edge;
Scalar duplicate_tolerance = process->duplicateVertexTolerance;
Scalar colinear_tolerance = process->colinearTolerance;
int total = 0;
//
//--------------------------------------------
// Delete all the error groups on this polygon
//--------------------------------------------
//
MStringChain group_list(NULL);
GetCollections(&group_list);
MStringChainIterator group_itr(&group_list);
PlugOf<MString> *group;
while ((group = group_itr.ReadAndNext()) != NULL)
{
Check_Object(group);
const char* name = group->GetItem();
Check_Pointer(name);
if (!_strnicmp(name, "_ERROR_", 7))
RemoveFromCollection(name);
Unregister_Object(group);
delete group;
}
//
//------------------------------------------------------------------------
// The first task we have is to find the two longest consecutive edges.
// We will use these edges to compute the desired polygon normal and plane
// equation
//------------------------------------------------------------------------
//
Point3D
position_a,
position_b = Point3D::Identity,
position_c = Point3D::Identity;
VertexProxy
*vertex_a = NULL,
*vertex_b = NULL,
*vertex_c = NULL;
Vector3D
edge_1,
edge_2 = Vector3D::Identity;
Plane
plane;
//
//-----------------------------------------------------------------
// Put in checks to make sure that the colors, normals, and uvs are
// compatible for all the vertices in this polygon
//-----------------------------------------------------------------
//
bool
uv_there = false,
color_there = false,
normal_there = false;
//
//-----------------------------------
// Spin through, testing the vertices
//-----------------------------------
//
DynamicArrayOf<IndexProxy*> indices;
unsigned index_count = UseIndexArray(&indices);
Verify(index_count == indices.GetLength());
Verify(index_count >= 3);
unsigned end=2;
for (unsigned i=0; i<index_count; ++i)
{
//
//-----------------------------------------------
// Generate all the information on the first pass
//-----------------------------------------------
//
if (!i)
{
Check_Object(indices[0]);
vertex_a = indices[0]->GetVertexProxy();
Check_Object(vertex_a);
Check_Object(indices[1]);
vertex_b = indices[1]->GetVertexProxy();
Check_Object(vertex_b);
Check_Object(indices[2]);
vertex_c = indices[2]->GetVertexProxy();
Check_Object(vertex_c);
vertex_a->GetPosition(&position_a);
vertex_b->GetPosition(&position_b);
vertex_c->GetPosition(&position_c);
edge_1.Subtract(position_b, position_a);
edge_2.Subtract(position_c, position_b);
Normal3D normal;
if (vertex_c->GetNormal(&normal))
{
if (!Close_Enough(normal.Vector3D::GetLengthSquared(), 1.0f, 2e-5f))
{
AddToCollection("_ERROR_Bad_Normal");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Bad_Normal
);
break;
}
}
//
//---------------------------------
// Set up the additional attributes
//---------------------------------
//
RGBAColor color;
color_there = vertex_c->GetColor(&color);
DynamicArrayOf<Vector2DOf<Scalar> > uv;
uv_there = vertex_c->GetUVs(&uv);
normal_there = vertex_c->GetNormal(&normal);
}
//
//--------------------------------------------------------------
// Get the index info. If this is not the first pass, copy the
// information from last pass
//--------------------------------------------------------------
//
else
{
Check_Object(vertex_b);
vertex_a = vertex_b;
Check_Object(vertex_c);
vertex_b = vertex_c;
if (++end >= index_count)
end -= index_count;
Check_Object(indices[end]);
vertex_c = indices[end]->GetVertexProxy();
Check_Object(vertex_c);
position_a = position_b;
position_b = position_c;
vertex_c->GetPosition(&position_c);
edge_1 = edge_2;
edge_2.Subtract(position_c, position_b);
//
//--------------------------------
// Check the additional attributes
//--------------------------------
//
RGBAColor color;
if (color_there != vertex_c->GetColor(&color))
{
AddToCollection("_ERROR_Mismatched_Vertex_Colors");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Mismatched_Vertex_Colors
);
break;
}
DynamicArrayOf<Vector2DOf<Scalar> > uv;
if (uv_there != vertex_c->GetUVs(&uv))
{
AddToCollection("_ERROR_Mismatched_UVs");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Mismatched_UVs
);
break;
}
Normal3D normal;
if (normal_there != vertex_c->GetNormal(&normal))
{
AddToCollection("_ERROR_Mismatched_Normals");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Mismatched_Normals
);
break;
}
}
//
//-----------------------------------------------------------------
// Check the forward leg length to see if the polygon has duplicate
// points
//-----------------------------------------------------------------
//
Scalar length = edge_2.GetLengthSquared();
if (
Small_Enough(length, duplicate_tolerance)
&& process->enableDuplicateCheck
)
{
AddToCollection("_ERROR_Duplicate_Vertex");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Duplicate_Vertex
);
break;
}
//
//---------------------------------------------------------------------
// Compute the cross-product of the two legs and check for colinearness.
// This is not necessarily bad, as long as we are coplanar
//---------------------------------------------------------------------
//
Vector3D v;
v.Cross(edge_1, edge_2);
Scalar cross_len = v.GetLength();
if (Small_Enough(cross_len, colinear_tolerance))
{
if (!i && process->enableDegenerateCheck)
{
AddToCollection("_ERROR_Degenerate_Polygon");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Degenerate_Polygon
);
break;
}
else if (
!Small_Enough(plane.GetDistanceTo(position_b), plane_tolerance)
&& process->enableCoplanarCheck
)
{
AddToCollection("_ERROR_Noncoplanar_Polygon");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Noncoplanar_Polygon
);
break;
}
continue;
}
//
//-------------------------------------------------------------------
// Normalize the cross. If this is the first pass, compute the plane
// equation for the polygon
//-------------------------------------------------------------------
//
cross_len = 1.0f / cross_len;
Normal3D edge_normal(v.x*cross_len, v.y*cross_len, v.z*cross_len);
if (!i)
{
plane.normal = edge_normal;
plane.offset = plane.normal * position_b;
}
else
{
//
//--------------------------
// Check for non-coplanarity
//--------------------------
//
if (
!Small_Enough(plane.GetDistanceTo(position_b), plane_tolerance)
&& process->enableCoplanarCheck
)
{
AddToCollection("_ERROR_Noncoplanar_Polygon");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Noncoplanar_Polygon
);
break;
}
//
//----------------------
// Check for concaveness
//----------------------
//
Scalar cosine = plane.normal*edge_normal;
if (cosine < colinear_tolerance && process->enableConvexCheck)
{
AddToCollection("_ERROR_Nonconvex_Polygon");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Noncoplanar_Polygon
);
break;
}
}
}
#if 0
//
//--------------------------------
// Check for polygons w/o textures
//--------------------------------
//
if(stateArray.GetLength() < 1)
{
AddToCollection("_ERROR_Nontextured_Polygon");
++total;
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Nontextured_Polygon
);
}
#endif
//
//-----------------------------
// Delete the remaining proxies
//-----------------------------
//
DetachArrayReferences(&indices);
return total;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TextureProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//------------------------------------------------------------
// Make sure that the process says its OK to check the texture
//------------------------------------------------------------
//
process->FindErrorsCallback(this, FindErrorsProcess::StatusCheck);
if (!process->continueProcess)
return 1;
//
//---------------------------------------------------------
// Make sure that the texture is between 32 and 256 in size
//---------------------------------------------------------
//
Vector2DOf<int> size;
GetImageSize(&size);
if (size.x<32 || size.x>512 || size.y<32 || size.y>512)
{
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Bad_Texture_Size
);
return 1;
}
//
//--------------------------------------------------------
// Make sure that we are dealing with a power of 2 texture
//--------------------------------------------------------
//
size.x ^= size.x&(-size.x);
size.y ^= size.y&(-size.y);
if (size.x || size.y)
{
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Bad_Texture_Size
);
return 1;
}
return 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
TextureLibrary::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//---------------------------
// Handle the texture library
//---------------------------
//
int total = 0;
TextureProxy *texture = UseFirstTextureProxy();
while (texture)
{
Check_Object(texture);
total += texture->FindErrors(process);
TextureProxy *next = texture->UseNextTextureProxyInLibrary();
texture->DetachReference();
if (!process->continueProcess)
{
if (next)
next->DetachReference();
return total;
}
texture = next;
}
return total;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
ChildProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
process->FindErrorsCallback(this, FindErrorsProcess::StatusCheck);
return (!process->continueProcess) ? 1 : 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
GroupProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//---------------------------------------------------
// Make sure that the process says its OK to continue
//---------------------------------------------------
//
process->FindErrorsCallback(this, FindErrorsProcess::StatusCheck);
if (!process->continueProcess)
return 1;
//
//-----------------------------------------------------
// Go through each child and count the number of errors
//-----------------------------------------------------
//
int total = 0;
ChildProxy *child = UseFirstChildProxy();
while (child)
{
Check_Object(child);
total += child->FindErrors(process);
ChildProxy *next = child->UseNextSiblingProxy();
child->DetachReference();
if (!process->continueProcess)
{
if (next)
next->DetachReference();
return total;
}
child = next;
}
return total;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
PolygonMeshProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//---------------------------------------------------
// Make sure that the process says its OK to continue
//---------------------------------------------------
//
process->FindErrorsCallback(this, FindErrorsProcess::StatusCheck);
if (!process->continueProcess)
return 1;
//
//-----------------------------------
// Count the number of polygon errors
//-----------------------------------
//
int total = 0;
DynamicArrayOf<PolygonProxy*> polygons;
unsigned polygon_count = UsePolygonArray(&polygons);
Verify(polygon_count == polygons.GetLength());
if (polygon_count)
{
for (unsigned i=0; i<polygon_count; ++i)
{
PolygonProxy *polygon = polygons[i];
Check_Object(polygon);
total += polygon->FindErrors(process);
}
}
else
{
process->FindErrorsCallback(
this,
FindErrorsProcess::ERROR_Empty_Mesh
);
total = 1;
}
DetachArrayReferences(&polygons);
return total;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
SceneProxy::FindErrors(FindErrorsProcess *process)
{
Check_Object(this);
Check_Object(process);
//
//---------------------------------------------------
// Make sure that the process says its OK to continue
//---------------------------------------------------
//
process->FindErrorsCallback(this, FindErrorsProcess::StatusCheck);
if (!process->continueProcess)
return 1;
//
//-------------------
// Check the textures
//-------------------
//
int total=0;
StateLibrary *states = GetStateLibrary();
Check_Object(states);
TextureLibrary *textures = states->GetTextureLibrary();
Check_Object(textures);
total += textures->FindErrors(process);
if (!process->continueProcess)
return total;
//
//-----------------
// Check each child
//-----------------
//
ChildProxy *child = UseFirstChildProxy();
while (child)
{
Check_Object(child);
total += child->FindErrors(process);
ChildProxy *next = child->UseNextSiblingProxy();
child->DetachReference();
if (!process->continueProcess)
{
if (next)
next->DetachReference();
break;
}
child = next;
}
return total;
}