plycrunch.cc
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:8k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. /*
  2.  * plycrunch.cc
  3.  * Scanalyze module incorporating the old standalone plycrunch decimator.
  4.  *
  5.  * Created: Greg Turk, August 1994
  6.  *
  7.  *   Simplify a polygon model by collapsing multiple points together.  This
  8.  *   is basically Jarek Rossignac's method of simplifying polygon models.
  9.  *
  10.  *   This code borrows heavily from "plyshared".
  11.  *
  12.  * Modified: Matt Ginzton, magi@cs, November 18 1998
  13.  *   converted to c++, incorporated into scanalyze
  14.  *
  15.  */
  16. #include <iostream.h>
  17. #include <vector.h>
  18. #include "Pnt3.h"
  19. struct Vertex {
  20.   Pnt3 pos;
  21.   int count;                    /* number of vertices that contributed */
  22.   int a,b,c;                    /* integer indices used in hash */
  23.   int index;
  24.   Vertex *shared;
  25.   Vertex *next;
  26. };
  27. /* hash table for near neighbor searches */
  28. #define PR1  17
  29. #define PR2 101
  30. static const int TABLE_SIZE[] = {
  31.   5003, 
  32.   17011,
  33.   53003,
  34.   107021,
  35.   233021,
  36.   472019,
  37.   600169,
  38.   1111189,
  39.   2222177,
  40.   4444147,
  41.   9374153,
  42.   20123119,
  43.   30123139,
  44.   50123011,
  45.   70123117,
  46.   100123171,
  47.   140123143,
  48.   200123111,
  49.   400123123,
  50.   800123119,
  51. };
  52. struct Hash_Table {         /* uniform spatial subdivision, with hash */
  53.   int npoints; /* number of points placed in table */
  54.   Vertex **verts; /* array of hash cells */
  55.   int num_entries; /* number of array elements in verts */
  56.   float scale; /* size of cell */
  57. };
  58. Hash_Table *init_table(int, float);
  59. void add_to_hash (Vertex *, Hash_Table *, float);
  60. void crunch_vertices (vector<Pnt3>& vtx, vector<int>& tris, float tolerance);
  61. float compute_average_edge_length (const vector<Pnt3>& vtx,
  62.    const vector<int>& tris);
  63. /******************************************************************************
  64. External interface
  65. ******************************************************************************/
  66. void plycrunch_simplify (const vector<Pnt3>& vtx, const vector<int>& tris,
  67.  vector<Pnt3>& outVtx, vector<int>& outTris,
  68.  int approxDesired)
  69. {
  70.   outVtx = vtx;
  71.   outTris = tris;
  72. #ifdef _ABI64
  73.   cerr << "N64 version can't do plycrunch!" << endl;
  74. #else
  75.   float avg_edge_length;
  76.   float tolerance;
  77.   // attempt to guess tolerance based on desired output count
  78.   avg_edge_length = compute_average_edge_length (vtx, tris);
  79.   cout << "edge len: " << avg_edge_length << endl;
  80.   // tolerance = edgelen * ratio: deletes approx. enough verts s.t.
  81.   // outtris = intris / ratio^2
  82.   // the 3 is arbitrary, by inspection
  83.   float ratio = sqrt (tris.size() / (3.*approxDesired));
  84.   tolerance = avg_edge_length * ratio;
  85.   crunch_vertices(outVtx, outTris, tolerance);
  86. #endif
  87. }
  88. /******************************************************************************
  89. Figure out which vertices should be collapsed into one.
  90. ******************************************************************************/
  91. void
  92. crunch_vertices (vector<Pnt3>& vtx, vector<int>& tris, float tolerance)
  93. {
  94. #ifndef _ABI64
  95.   int i,j;
  96.   int jj;
  97.   Hash_Table *table;
  98.   float squared_dist;
  99.   Vertex *vert;
  100.   table = init_table (vtx.size(), tolerance);
  101.   squared_dist = tolerance * tolerance;
  102.   /* place all vertices in the hash table, and in the process */
  103.   /* learn which ones should be collapsed */
  104.   int nVerts = vtx.size();
  105.   int nTris = tris.size();
  106.   vector<Vertex> vlist (nVerts);
  107.   for (i = 0; i < nVerts; i++) {
  108.     vlist[i].count = 1;
  109.     vlist[i].pos = vtx[i];
  110.     add_to_hash (&vlist[i], table, squared_dist);
  111.   }
  112.   vector<bool> used (nTris / 3, true);
  113.   /* compute average of all coordinates that contributed to */
  114.   /* the vertices placed in the hash table */
  115.   for (i = 0; i < nVerts; i++) {
  116.     vert = &vlist[i];
  117.     if (vert->shared == vert) {
  118.       vert->pos /= vert->count;
  119.     }
  120.   }
  121.   /* fix up the faces to point to the collapsed vertices */
  122.   for (i = 0; i < nTris; i += 3) {
  123.     
  124.     /* change all indices to pointers to the collapsed vertices */
  125.     for (j = 0; j < 3; j++)
  126.       tris[i+j] = (int) (vlist[tris[i+j]].shared);
  127.     /* collapse adjacent vertices in a face that are the same */
  128.     for (j = 2; j >= 0; j--) {
  129.       jj = (j+1) % 3;
  130.       if (tris[i+j] == tris[i+jj]) {
  131. used[i/3] = false;
  132.       }
  133.     }
  134.   }
  135.   // recreate geometry/face array
  136.   int nOutVerts = 0;
  137.   for (i = 0; i < nVerts; i++) {
  138.     if (vlist[i].shared == &vlist[i]) {
  139.       vlist[i].index = nOutVerts++;
  140.     }
  141.   }
  142.   cout << "Output mesh vertices: " << nOutVerts << endl;
  143.   vtx.clear();
  144.   vtx.reserve (nOutVerts);
  145.   for (i = 0; i < nVerts; i++) {
  146.     if (vlist[i].shared == &vlist[i])
  147.       vtx.push_back (vlist[i].pos);
  148.   }
  149.   int nOutTris = 0;
  150.   int nt = nTris / 3;
  151.   for (i = 0; i < nt; i ++) {
  152.     if (used[i])
  153.       nOutTris += 3;
  154.   }
  155.   cout << "Output mesh tris: " << nOutTris << endl;
  156.   vector<int> otris;
  157.   otris.reserve (nOutTris);
  158.   for (i = 0; i < nTris; i += 3) {
  159.     if (used[i/3]) {
  160.       otris.push_back (((Vertex*)tris[i])->index);
  161.       otris.push_back (((Vertex*)tris[i+1])->index);
  162.       otris.push_back (((Vertex*)tris[i+2])->index);
  163.     }
  164.   }
  165.   tris = otris;
  166. #endif
  167. }
  168.  
  169. /******************************************************************************
  170. Compute the average edge length.  Currently loops through faces and
  171. visits all shared edges twice, giving them double wieghting with respect
  172. to boundary edges.
  173. ******************************************************************************/
  174. float 
  175. compute_average_edge_length (const vector<Pnt3>& vtx, const vector<int>& tris)
  176. {
  177.   float total_length = 0;
  178.   int nTris = tris.size();
  179.   if (!nTris)
  180.     return 0;
  181.   for (int i = 0; i < nTris; i += 3) {
  182.     for (int j = 0; j < 3; j++) {
  183.       int jj = (j+1) % 3;
  184.       const Pnt3& v1 = vtx[tris[i+j]];
  185.       const Pnt3& v2 = vtx[tris[i+jj]];
  186.       float norm2 = (v1-v2).norm2();
  187.       if (norm2) { // avoid sqrt(0)
  188. total_length += sqrtf (norm2);
  189.       }
  190.     }
  191.   }
  192.   return total_length/nTris;
  193. }
  194. /******************************************************************************
  195. Add a vertex to it's hash table.
  196. Entry:
  197.   vert    - vertex to add
  198.   table   - hash table to add to
  199.   sq_dist - squared value of distance tolerance
  200. ******************************************************************************/
  201. void 
  202. add_to_hash(Vertex *vert, Hash_Table *table, float sq_dist)
  203. {
  204.   int index;
  205.   int a,b,c;
  206.   float scale;
  207.   Vertex *ptr;
  208.   /* determine which cell the position lies within */
  209.   scale = table->scale;
  210.   a = floor (vert->pos[0] * scale);
  211.   b = floor (vert->pos[1] * scale);
  212.   c = floor (vert->pos[2] * scale);
  213.   index = (a * PR1 + b * PR2 + c) % table->num_entries;
  214.   if (index < 0)
  215.     index += table->num_entries;
  216.   /* examine all points hashed to this cell, looking for */
  217.   /* a vertex to collapse with */
  218.   for (ptr = table->verts[index]; ptr != NULL; ptr = ptr->next)
  219.     if (a == ptr->a && b == ptr->b && c == ptr->c) {
  220.       /* add to sums of coordinates (that later will be averaged) */
  221.       ptr->pos += vert->pos;
  222.       ptr->count++;
  223.       vert->shared = ptr;
  224.       return;
  225.     }
  226.   /* no match if we get here, so add new hash table entry */
  227.   vert->next = table->verts[index];
  228.   table->verts[index] = vert;
  229.   vert->shared = vert;  /* self-reference as close match */
  230.   vert->a = a;
  231.   vert->b = b;
  232.   vert->c = c;
  233. }
  234. /******************************************************************************
  235. Initialize a uniform spatial subdivision table.  This structure divides
  236. 3-space into cubical cells and deposits points into their appropriate
  237. cells.  It uses hashing to make the table a one-dimensional array.
  238. Entry:
  239.   nverts - number of vertices that will be placed in the table
  240.   size   - size of a cell
  241. Exit:
  242.   returns pointer to hash table
  243. ******************************************************************************/
  244. Hash_Table *
  245. init_table(int nverts, float size)
  246. {
  247.   Hash_Table *table;
  248.   float scale;
  249.   /* allocate new hash table */
  250.   table = (Hash_Table *) malloc (sizeof (Hash_Table));
  251.   table->num_entries = 0;
  252.   const int nTableSizes = sizeof(TABLE_SIZE) / sizeof(TABLE_SIZE[0]);
  253.   for (int ii = 0; ii < nTableSizes; ii++) {
  254.     if (nverts < TABLE_SIZE[ii]) {
  255.       table->num_entries = TABLE_SIZE[ii];
  256.       break;
  257.     }
  258.   }
  259.   if (!table->num_entries) {
  260.     cout << "Warning: hash table was not prepared for this many vertices"
  261.  << endl;
  262.     table->num_entries = TABLE_SIZE[nTableSizes-1];
  263.   }
  264.   table->verts = (Vertex **) malloc (sizeof (Vertex *) * table->num_entries);
  265.   /* set all table elements to NULL */
  266.   for (int i = 0; i < table->num_entries; i++)
  267.     table->verts[i] = NULL;
  268.   /* place each point in table */
  269.   scale = 1 / size;
  270.   table->scale = scale;
  271.   return (table);
  272. }