590 lines
12 KiB
C++
590 lines
12 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "core/geometry_reducer.h"
|
|
#include "core/geometry.h"
|
|
|
|
using namespace GS::Core;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GeometryReducer::RemoveVertex (uint v)
|
|
{
|
|
vlist[v].active = false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
float GeometryReducer::ComputeEdgeCost(Geometry *sg, nLLEDGE *edg)
|
|
{
|
|
#define nMAXTRIPEREDGE 512 // FIXME
|
|
|
|
nLLTRI *sidetri[nMAXTRIPEREDGE];
|
|
nTENTRY *te;
|
|
|
|
uint nsidetri = 0, n;
|
|
|
|
te = tlist.lut[edg->a];
|
|
while (te)
|
|
{
|
|
if (te->tri->UseVertex(edg->b))
|
|
sidetri[nsidetri++] = te->tri;
|
|
te = te->n;
|
|
}
|
|
|
|
float curvature = 0.f;
|
|
|
|
te = tlist.lut[edg->a];
|
|
|
|
while (te)
|
|
{
|
|
float mincurv = 1.f;
|
|
|
|
for (n = 0; n < nsidetri; ++n)
|
|
{
|
|
float dot = te->tri->normal.Dot(sidetri[n]->normal);
|
|
dot = (1.f - dot) / 2.f;
|
|
if (dot < mincurv)
|
|
mincurv = dot;
|
|
}
|
|
if (mincurv > curvature)
|
|
curvature = mincurv;
|
|
te = te->n;
|
|
}
|
|
|
|
return curvature * Vector4::Dist(sg->vtx[edg->a], sg->vtx[edg->b]);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GeometryReducer::ComputeVertexCost(Geometry *sg, uint v)
|
|
{
|
|
nEENTRY *te = elist.lut[v];
|
|
vlist[v].cost = -1.f;
|
|
|
|
// cheapest collapse target for this vertex...
|
|
while (te)
|
|
{
|
|
float ecost = ComputeEdgeCost (sg, te->edge);
|
|
if ( (vlist[v].cost == -1.f) || (ecost < vlist[v].cost) )
|
|
{
|
|
vlist[v].tgtcollapse = te->edge->b;
|
|
vlist[v].cost = ecost;
|
|
}
|
|
te = te->n;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void nLLTRI::ReplaceVertex(uint f, uint t)
|
|
{
|
|
if ( a == f ) a = t;
|
|
else if ( b == f ) b = t;
|
|
else if ( c == f ) c = t;
|
|
}
|
|
char nLLTRI::UseVertex(uint i)
|
|
{
|
|
if ((a == i) || (b == i) || (c == i))
|
|
return true;
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
char GeometryReducer::IsBorder(uint v)
|
|
{
|
|
/*
|
|
If any of the edge going trough the vertex owns only one polygon then
|
|
the vertex is on a border...
|
|
`*/
|
|
for (nEENTRY *pedg = elist.lut[v]; pedg; pedg = pedg->n)
|
|
{
|
|
uint ecnt = 0;
|
|
for (nTENTRY *ptri = tlist.lut[v]; ptri; ptri = ptri->n)
|
|
if (ptri->tri->UseVertex (pedg->edge->b))
|
|
++ecnt;
|
|
|
|
if (ecnt < 2)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Geometry *GeometryReducer::Reduce(Geometry *sg, float k)
|
|
{
|
|
if (!sg->pol.GetCount()) return NULL;
|
|
if (k <= 0.f) return NULL;
|
|
if (k >= 1.f) return NULL;
|
|
|
|
#ifdef DEBUG_COMPILATION
|
|
__LOG__ << "Geometry reducer invoked: " << k << "...\n";
|
|
#endif
|
|
|
|
// Allocate
|
|
Geometry *ng = new Geometry;
|
|
if (!ng)
|
|
return NULL;
|
|
|
|
// Triangulate geometry.
|
|
uint n, m;
|
|
nLLTRI *ctri;
|
|
|
|
elist.SetVertexCount (sg->vtx.GetCount());
|
|
tlist.SetGeo (sg);
|
|
|
|
for (n = 0; n < sg->pol.GetCount(); ++n)
|
|
{
|
|
const Polygon &p = sg->pol[n];
|
|
|
|
for (m = 1; m < (uint)(p.vtx_count - 1); ++m)
|
|
{
|
|
ctri = tlist.Add(p.binding[0], p.binding[m], p.binding[m+1]);
|
|
ctri->m = p.material;
|
|
tlist.ComputeNormal(ctri);
|
|
|
|
// insert edges
|
|
elist.Add(p.binding[0], p.binding[m]);
|
|
elist.Add(p.binding[m], p.binding[m+1]);
|
|
|
|
elist.Add(p.binding[m], p.binding[0]);
|
|
elist.Add(p.binding[m+1], p.binding[m]);
|
|
}
|
|
|
|
elist.Add(p.binding[0], p.binding[m]);
|
|
elist.Add(p.binding[m], p.binding[0]);
|
|
}
|
|
|
|
// create vertice list
|
|
vlist = new nLVERTEX[sg->vtx.GetCount()];
|
|
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
|
{
|
|
vlist[n].active = true;
|
|
vlist[n].locked = IsBorder (n);
|
|
vlist[n].tgtcollapse = n;
|
|
ComputeVertexCost (sg, n);
|
|
}
|
|
|
|
// collapse until we reach target...
|
|
uint _tgt = (uint)((float)tlist.ntri * k);
|
|
#ifdef DEBUG_COMPILATION
|
|
__LOG__ << "Collapsing from " << tlist.ntri << " to " << _tgt << "...\n";
|
|
float pcttri = (float)(tlist.ntri - _tgt) * 0.01f;
|
|
#endif
|
|
|
|
while ( tlist.ntri > _tgt )
|
|
{
|
|
// get the cheapest vertex to collapse
|
|
int vtx = -1;
|
|
float cst = 10000000.f;
|
|
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
|
{
|
|
if ( vlist[n].active && (!vlist[n].locked) && (vlist[n].cost < cst) )
|
|
{
|
|
vtx = n;
|
|
cst = vlist[n].cost;
|
|
}
|
|
}
|
|
|
|
/* // move vertex
|
|
if ( !vlist[vlist[vtx].tgtcollapse].locked )
|
|
{
|
|
VEC_INC (sg->vtx[vlist[vtx].tgtcollapse], sg->vtx[vtx]);
|
|
VEC_SCALEK (sg->vtx[vlist[vtx].tgtcollapse], 0.5f);
|
|
}
|
|
*/
|
|
// remap triangles
|
|
tlist.ReplaceVertex (vtx, vlist[vtx].tgtcollapse);
|
|
elist.RemapEdges (vtx, vlist[vtx].tgtcollapse);
|
|
|
|
// recompute cost for all modified vertice
|
|
ComputeVertexCost (sg, vlist[vtx].tgtcollapse);
|
|
|
|
nEENTRY *pedg = elist.lut[vlist[vtx].tgtcollapse];
|
|
while ( pedg )
|
|
{
|
|
ComputeVertexCost (sg, pedg->edge->b);
|
|
pedg = pedg->n;
|
|
}
|
|
|
|
#ifdef DEBUG_COMPILATION
|
|
if (!(tlist.ntri & 4095))
|
|
__LOG__ << (float)(100.f - ((float)(tlist.ntri - _tgt) / pcttri)) << "%%...\n";
|
|
#endif
|
|
// Invalidate vertex.
|
|
vlist[vtx].active = false;
|
|
}
|
|
|
|
_safe_delete_array(vlist);
|
|
|
|
#ifdef DEBUG_COMPILATION
|
|
__LOG__ << "Geometry reduction done.\n";
|
|
#endif
|
|
|
|
// Convert to mesh datas...
|
|
char *usevtx = new char[sg->vtx.GetCount()];
|
|
GS::Memory::Set(usevtx, 0, sg->vtx.GetCount());
|
|
|
|
ctri = tlist.root;
|
|
while ( ctri )
|
|
{
|
|
usevtx[ctri->a] = true;
|
|
usevtx[ctri->b] = true;
|
|
usevtx[ctri->c] = true;
|
|
ctri = ctri->n;
|
|
}
|
|
|
|
m = 0;
|
|
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
|
if ( usevtx[n] )
|
|
m++;
|
|
|
|
// Fill new geometry.
|
|
ng->pol.Allocate(tlist.ntri);
|
|
ng->binding.Allocate(ng->pol.GetCount() * 3);
|
|
ng->vtx.Allocate(m);
|
|
uint *rmpvtx = new uint[sg->vtx.GetCount()];
|
|
|
|
// Copy vertice.
|
|
m = 0;
|
|
for ( n = 0; n < sg->vtx.GetCount(); n++ )
|
|
if ( usevtx[n] )
|
|
{
|
|
rmpvtx[n] = m;
|
|
ng->vtx[m++] = sg->vtx[n];
|
|
}
|
|
_safe_delete_array(usevtx);
|
|
|
|
// Setup polygons.
|
|
m = 0;
|
|
ctri = tlist.root;
|
|
for ( n = 0; n < ng->pol.GetCount(); n++ )
|
|
{
|
|
ng->pol[n].vtx_count = 3;
|
|
ng->pol[n].binding = &ng->binding[m];
|
|
ng->binding[m++] = rmpvtx[ctri->a];
|
|
ng->binding[m++] = rmpvtx[ctri->b];
|
|
ng->binding[m++] = rmpvtx[ctri->c];
|
|
ng->pol[n].material = ctri->m;
|
|
ctri = ctri->n;
|
|
}
|
|
_safe_delete_array(rmpvtx);
|
|
|
|
// Copy material.
|
|
if (!ng->material_table.Allocate(sg->material_table.GetCount()))
|
|
return NULL;
|
|
for (n = 0; n < ng->material_table.GetCount(); n++)
|
|
ng->material_table[n] = sg->material_table[n];
|
|
|
|
// Setup geometry.
|
|
ng->ComputeVertexNormal();
|
|
|
|
return ng;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------
|
|
void nLTRILIST::ComputeNormal (nLLTRI *ctri)
|
|
//---------------------------------------------------
|
|
{
|
|
Vector4 va = sg->vtx[ctri->c] - sg->vtx[ctri->a];
|
|
Vector4 vb = sg->vtx[ctri->b] - sg->vtx[ctri->a];
|
|
ctri->normal = vb.Cross(va).Normalized();
|
|
}
|
|
|
|
//--------------------------------------------
|
|
void nLTRILIST::SetGeo (Geometry *g)
|
|
//--------------------------------------------
|
|
{
|
|
sg = g;
|
|
lut = new pnTENTRY[g->vtx.GetCount()];
|
|
for ( uint c = 0; c < g->vtx.GetCount(); c++ )
|
|
lut[c] = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
nLLTRI *nLTRILIST::Add (uint a, uint b, uint c)
|
|
//-----------------------------------------------------------
|
|
{
|
|
nLLTRI *tri = new nLLTRI;
|
|
tri->a = a;
|
|
tri->b = b;
|
|
tri->c = c;
|
|
|
|
tri->n = root;
|
|
tri->p = NULL;
|
|
if ( root )
|
|
root->p = tri;
|
|
root = tri;
|
|
|
|
// Register in vertex to poly.
|
|
AddTriToVertex(tri, a);
|
|
AddTriToVertex(tri, b);
|
|
AddTriToVertex(tri, c);
|
|
ntri++;
|
|
|
|
return tri;
|
|
}
|
|
|
|
//--------------------------------------------
|
|
void nLTRILIST::Remove(nLLTRI *t)
|
|
//--------------------------------------------
|
|
{
|
|
RemoveTriFromVertex(t, t->a);
|
|
RemoveTriFromVertex(t, t->b);
|
|
RemoveTriFromVertex(t, t->c);
|
|
|
|
if (t->p)
|
|
t->p->n = t->n;
|
|
else root = t->n;
|
|
if (t->n)
|
|
t->n->p = t->p;
|
|
delete t;
|
|
ntri--;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
void nLTRILIST::RemoveTriFromVertex(nLLTRI *t, uint v)
|
|
//--------------------------------------------------------------------
|
|
{
|
|
nTENTRY *n = lut[v], *p = NULL;
|
|
while (n)
|
|
{
|
|
if (n->tri == t)
|
|
break;
|
|
p = n;
|
|
n = n->n;
|
|
}
|
|
if (!n)
|
|
return;
|
|
if (!p)
|
|
lut[v] = n->n;
|
|
else p->n = n->n;
|
|
_safe_delete(n);
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
void nLTRILIST::AddTriToVertex (nLLTRI *t, uint v)
|
|
//---------------------------------------------------------------
|
|
{
|
|
nTENTRY *n = lut[v];
|
|
while ( n && (n->tri != t ))
|
|
n = n->n;
|
|
if ( n ) return;
|
|
|
|
n = new nTENTRY;
|
|
n->tri = t;
|
|
n->n = lut[v];
|
|
lut[v] = n;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
void nLTRILIST::ReplaceVertex (uint a, uint b)
|
|
//----------------------------------------------------------
|
|
{
|
|
nTENTRY *s = lut[a], *n;
|
|
while ( s )
|
|
{
|
|
n = s->n;
|
|
|
|
if ( s->tri->UseVertex (b) )
|
|
Remove (s->tri);
|
|
else
|
|
{
|
|
s->tri->ReplaceVertex (a, b);
|
|
AddTriToVertex (s->tri, b);
|
|
ComputeNormal (s->tri);
|
|
RemoveTriFromVertex (s->tri, a);
|
|
}
|
|
|
|
s = n;
|
|
}
|
|
}
|
|
|
|
//---------------------------
|
|
nLTRILIST::nLTRILIST ()
|
|
//---------------------------
|
|
{
|
|
ntri = 0;
|
|
root = NULL;
|
|
}
|
|
|
|
//----------------------------
|
|
nLTRILIST::~nLTRILIST ()
|
|
//----------------------------
|
|
{
|
|
uint i;
|
|
for ( i = 0; i < sg->vtx.GetCount(); i++ )
|
|
{
|
|
nTENTRY *s = lut[i], *n;
|
|
while ( s )
|
|
{
|
|
n = s->n;
|
|
delete s;
|
|
s = n;
|
|
}
|
|
}
|
|
delete [] lut;
|
|
lut = NULL;
|
|
|
|
nLLTRI *s = root, *n;
|
|
while ( s )
|
|
{
|
|
n = s->n;
|
|
delete s;
|
|
s = n;
|
|
}
|
|
root = NULL;
|
|
ntri = 0;
|
|
}
|
|
|
|
//--------------------------------------------
|
|
void nEDGELIST::Add (uint a, uint b)
|
|
//--------------------------------------------
|
|
{
|
|
nEENTRY *pedg;
|
|
if ( a == b )
|
|
return;
|
|
|
|
pedg = lut[a];
|
|
while ( pedg )
|
|
{
|
|
if ( pedg->edge->b == b )
|
|
return;
|
|
pedg = pedg->n;
|
|
}
|
|
|
|
nLLEDGE *edg = new nLLEDGE;
|
|
edg->a = a;
|
|
edg->b = b;
|
|
|
|
edg->n = root;
|
|
edg->p = NULL;
|
|
if ( root )
|
|
root->p = edg;
|
|
root = edg;
|
|
|
|
// update lut
|
|
pedg = new nEENTRY;
|
|
pedg->edge = edg;
|
|
pedg->n = lut[a];
|
|
lut[a] = pedg;
|
|
nedg++;
|
|
}
|
|
|
|
//--------------------------------------------
|
|
void nEDGELIST::Remove (nLLEDGE *edg)
|
|
//--------------------------------------------
|
|
{
|
|
if ( !edg )
|
|
return;
|
|
if ( edg->n )
|
|
edg->n->p = edg->p;
|
|
if ( edg->p )
|
|
edg->p->n = edg->n;
|
|
else root = edg->n;
|
|
|
|
// update lut
|
|
nEENTRY *pedg = lut[edg->a], *ledg = NULL;
|
|
while ( pedg )
|
|
{
|
|
if ( pedg->edge == edg )
|
|
break;
|
|
ledg = pedg;
|
|
pedg = pedg->n;
|
|
}
|
|
if ( pedg )
|
|
{
|
|
if ( ledg )
|
|
ledg->n = pedg->n;
|
|
else lut[edg->a] = pedg->n;
|
|
delete pedg;
|
|
}
|
|
delete edg;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
nLLEDGE *nEDGELIST::GetEdge (uint a, uint b)
|
|
//-----------------------------------------------------
|
|
{
|
|
nEENTRY *pedg = lut[a];
|
|
while ( pedg && (pedg->edge->b != b) )
|
|
pedg = pedg->n;
|
|
if ( !pedg )
|
|
return NULL;
|
|
return pedg->edge;
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
void nEDGELIST::RemapEdges (uint a, uint b)
|
|
//---------------------------------------------------
|
|
{
|
|
nEENTRY *pedg = lut[a], *nedg;
|
|
|
|
// remap all edges and wipe invalid ones
|
|
while ( pedg )
|
|
{
|
|
uint ob = pedg->edge->b;
|
|
nedg = pedg->n;
|
|
|
|
Remove (GetEdge (pedg->edge->b, pedg->edge->a));
|
|
Remove (pedg->edge);
|
|
|
|
Add (b, ob);
|
|
Add (ob, b);
|
|
|
|
pedg = nedg;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------
|
|
void nEDGELIST::SetVertexCount (uint v)
|
|
//---------------------------------------------
|
|
{
|
|
lut = new pnEENTRY[v];
|
|
for ( uint n = 0; n < v; n++ )
|
|
lut[n] = NULL;
|
|
vtx_count = v;
|
|
}
|
|
|
|
//---------------------------
|
|
nEDGELIST::nEDGELIST ()
|
|
//---------------------------
|
|
{
|
|
lut = NULL;
|
|
root = NULL;
|
|
nedg = 0;
|
|
}
|
|
|
|
//----------------------------
|
|
nEDGELIST::~nEDGELIST ()
|
|
//----------------------------
|
|
{
|
|
uint n;
|
|
for (n = 0; n < vtx_count; n++)
|
|
{
|
|
nEENTRY *pedg = lut[n], *nedg;
|
|
while (pedg)
|
|
{
|
|
nedg = pedg->n;
|
|
delete pedg;
|
|
pedg = nedg;
|
|
}
|
|
}
|
|
_safe_delete_array(lut);
|
|
|
|
nLLEDGE *pedg = root, *nedg;
|
|
while (pedg)
|
|
{
|
|
nedg = pedg->n;
|
|
delete pedg;
|
|
pedg = nedg;
|
|
}
|
|
root = NULL;
|
|
}
|