Files
firestorm/Gameleap/code/mw4/Tools/TCTb/Terra/Subdivision.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

430 lines
8.4 KiB
C++

#include <stdlib.h>
#include <iostream.h>
#include <assert.h>
#include "Subdivision.hpp"
Edge *Subdivision::makeEdge(Vec2& org, Vec2& dest)
{
Edge *e = new Edge();
e->EndPoints(org, dest);
return e;
}
Edge *Subdivision::makeEdge()
{
return new Edge();
}
void Subdivision::initMesh(const Vec2& A,const Vec2& B,
const Vec2& C,const Vec2& D)
{
Vec2& a = A.clone();
Vec2& b = B.clone();
Vec2& c = C.clone();
Vec2& d = D.clone();
Edge *ea = makeEdge();
ea->EndPoints(a, b);
Edge *eb = makeEdge();
splice(ea->Sym(), eb);
eb->EndPoints(b, c);
Edge *ec = makeEdge();
splice(eb->Sym(), ec);
ec->EndPoints(c, d);
Edge *ed = makeEdge();
splice(ec->Sym(), ed);
ed->EndPoints(d, a);
splice(ed->Sym(), ea);
Edge *diag = makeEdge();
splice(ed->Sym(),diag);
splice(eb->Sym(),diag->Sym());
diag->EndPoints(a,c);
startingEdge = ea;
first_face = NULL;
makeFace(ea->Sym()).update(*this);
makeFace(ec->Sym()).update(*this);
}
void Subdivision::deleteEdge(Edge *e)
{
splice(e, e->Oprev());
splice(e->Sym(), e->Sym()->Oprev());
delete e;
}
Edge *Subdivision::connect(Edge *a, Edge *b)
{
Edge *e = makeEdge();
splice(e, a->Lnext());
splice(e->Sym(), b);
e->EndPoints(a->Dest(), b->Org());
return e;
}
void Subdivision::swap(Edge *e)
{
Triangle *f1 = e->Lface();
Triangle *f2 = e->Sym()->Lface();
Edge *a = e->Oprev();
Edge *b = e->Sym()->Oprev();
splice(e, a);
splice(e->Sym(), b);
splice(e, a->Lnext());
splice(e->Sym(), b->Lnext());
e->EndPoints(a->Dest(), b->Dest());
f1->reshape(e);
f2->reshape(e->Sym());
}
//
// Subdivision iterators
//
static unsigned int timestamp = 0;
static void overEdge(Edge *e, edge_callback fn, void *closure)
{
if( e->token != timestamp )
{
e->token = timestamp;
e->Sym()->token = timestamp;
(*fn)(e, closure);
overEdge(e->Onext(), fn, closure);
overEdge(e->Oprev(), fn, closure);
overEdge(e->Dnext(), fn, closure);
overEdge(e->Dprev(), fn, closure);
}
}
void Subdivision::overEdges(edge_callback fn, void *closure)
{
if( ++timestamp == 0 )
timestamp = 1;
overEdge(startingEdge, fn, closure);
}
void Subdivision::overFaces(face_callback fn, void *closure)
{
Triangle *t = first_face;
while( t )
{
(*fn)(*t, closure);
t = t->getLink();
}
}
//
// Random predicates
//
bool Subdivision::ccwBoundary(const Edge *e)
{
return !rightOf(e->Oprev()->Dest(), e);
}
bool Subdivision::onEdge(const Vec2& x, Edge *e)
{
real t1, t2, t3;
t1 = (x - e->Org()).length();
t2 = (x - e->Dest()).length();
if (t1 < EPS || t2 < EPS)
return true;
t3 = (e->Org() - e->Dest()).length();
if (t1 > t3 || t2 > t3)
return false;
Line line(e->Org(), e->Dest());
return (fabs(line.eval(x)) < EPS);
}
bool Subdivision::isInterior(Edge *e)
//
// Tests whether e is an interior edge.
//
// WARNING: This topological test will not work if the boundary is
// a triangle. This is not a problem here; the boundary is
// always a rectangle. But if you try to adapt this code, please
// keep this in mind.
{
return (e->Lnext()->Lnext()->Lnext() == e &&
e->Rnext()->Rnext()->Rnext() == e );
}
bool Subdivision::shouldSwap(const Vec2& x, Edge *e)
{
Edge *t = e->Oprev();
return inCircle(e->Org(), t->Dest(), e->Dest(), x);
}
Edge *Subdivision::locate(const Vec2& x, Edge *start)
{
Edge *e = start;
real t = triArea(x, e->Dest(), e->Org());
if (t>0) { // x is to the right of edge e
t = -t;
e = e->Sym();
}
while (true)
{
Edge *eo = e->Onext();
Edge *ed = e->Dprev();
real to = triArea(x, eo->Dest(), eo->Org());
real td = triArea(x, ed->Dest(), ed->Org());
if (td>0) // x is below ed
if (to>0 || to==0 && t==0) {// x is interior, or origin endpoint
startingEdge = e;
return e;
}
else { // x is below ed, below eo
t = to;
e = eo;
}
else // x is on or above ed
if (to>0) // x is above eo
if (td==0 && t==0) { // x is destination endpoint
startingEdge = e;
return e;
}
else { // x is on or above ed and above eo
t = td;
e = ed;
}
else // x is on or below eo
if (t==0 && !leftOf(eo->Dest(), e))
// x on e but subdiv. is to right
e = e->Sym();
else if (rand()&1) { // x is on or above ed and
t = to; // on or below eo; step randomly
e = eo;
}
else {
t = td;
e = ed;
}
}
}
Edge *Subdivision::spoke(Vec2& x, Edge *e)
{
Triangle *new_faces[4];
int facedex = 0;
//
// NOTE: e is the edge returned by locate(x)
//
if ( (x == e->Org()) || (x == e->Dest()) ) {
// point is already in the mesh
//
/*
cerr << "WARNING: Tried to reinsert point: " << x << endl;
cerr << " org: " << e->Org() << endl;
cerr << " dest: " << e->Dest() << endl;
*/
return NULL;
}
Edge *boundary_edge = NULL;
Triangle *lface = e->Lface();
lface->dontAnchor(e);
new_faces[facedex++] = lface;
if( onEdge(x,e) )
{
if( ccwBoundary(e) ) {
//
// e lies on the boundary
// Defer deletion until after new edges are added.
boundary_edge = e;
}
else {
Triangle *sym_lface = e->Sym()->Lface();
new_faces[facedex++] = sym_lface;
sym_lface->dontAnchor(e->Sym());
e = e->Oprev();
deleteEdge(e->Onext());
}
}
else
{
// x lies within the Lface of e
}
Edge *base = makeEdge(e->Org(), x.clone());
splice(base, e);
startingEdge = base;
do {
base = connect(e, base->Sym());
e = base->Oprev();
} while( e->Lnext() != startingEdge );
if( boundary_edge )
deleteEdge(boundary_edge);
// Update all the faces in our new spoked polygon.
// If point x on perimeter, then don't add an exterior face
base = boundary_edge ? startingEdge->Rprev() : startingEdge->Sym();
do {
if( facedex )
new_faces[--facedex]->reshape(base);
else
makeFace(base);
base = base->Onext();
} while( base != startingEdge->Sym() );
return startingEdge;
}
//
// s is a spoke pointing OUT from x
//
void Subdivision::optimize(Vec2& x, Edge *s)
{
Edge *start_spoke = s;
Edge *spoke = s;
do {
Edge *e = spoke->Lnext();
Edge *t = e->Oprev();
if( isInterior(e) && shouldSwap(x, e) )
swap(e);
else
{
spoke = spoke->Onext();
if( spoke == start_spoke )
break;
}
} while( true );
//
// Now, update all the triangles
spoke = start_spoke;
do {
Edge *e = spoke->Lnext();
Triangle *t = e->Lface();
if( t ) t->update(*this);
spoke = spoke->Onext();
} while( spoke != start_spoke );
}
Edge *Subdivision::insert(Vec2& x, Triangle *tri)
{
Edge *e = tri?locate(x, tri->getAnchor()):locate(x);
Edge *start_spoke = spoke(x, e);
if( start_spoke )
optimize(x, start_spoke->Sym());
return start_spoke;
}
Triangle *Subdivision::allocFace(Edge *e)
{
return new Triangle(e);
}
Triangle& Subdivision::makeFace(Edge *e)
{
Triangle *t = allocFace(e);
first_face = t->linkTo(first_face);
return *t;
}
void Triangle::dontAnchor(Edge *e)
{
if( anchor == e )
{
anchor = e->Lnext();
}
}
void Triangle::reshape(Edge *e)
{
anchor = e;
e->set_Lface(this);
e->Lnext()->set_Lface(this);
e->Lprev()->set_Lface(this);
}
void Triangle::update(Subdivision&)
// called by reshape to update stuff
//
// the default method will do nothing
{
}