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