Files
Webcam/include/engine/core/geometry_reducer.cpp

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;
}