2 * Polygon Reduction Demo by Stan Melax (c) 1998
\r
3 * Permission to use any of this code wherever you want is granted..
\r
4 * Although, please do acknowledge authorship if appropriate.
\r
6 * This module initializes the bunny model data and calls
\r
7 * the polygon reduction routine. At each frame the RenderModel()
\r
8 * routine is called to draw the model. This module also
\r
9 * animates the parameters (such as number of vertices to
\r
10 * use) to show the model at various levels of detail.
\r
13 #include <windows.h>
\r
20 #pragma warning(disable : 4244)
\r
24 #include "progmesh.h"
\r
25 #include "rabdata.h"
\r
27 extern float DeltaT; // change in time since last frame
\r
28 int render_num; // number of vertices to draw with
\r
29 float lodbase=0.5f; // the fraction of vertices used to morph toward
\r
30 float morph=1.0f; // where to render between 2 levels of detail
\r
31 List<Vector> vert; // global list of vertices
\r
32 List<tridata> tri; // global list of triangles
\r
33 List<int> collapse_map; // to which neighbor each vertex collapses
\r
34 int renderpolycount=0; // polygons rendered in the current frame
\r
35 Vector model_position; // position of bunny
\r
36 Quaternion model_orientation; // orientation of bunny
\r
38 // Note that the use of the Map() function and the collapse_map
\r
39 // list isn't part of the polygon reduction algorithm.
\r
40 // We just set up this system here in this module
\r
41 // so that we could retrieve the model at any desired vertex count.
\r
42 // Therefore if this part of the program confuses you, then
\r
43 // dont worry about it. It might help to look over the progmesh.cpp
\r
48 // When the model is rendered using a maximum of mx vertices
\r
49 // then it is vertices 0 through mx-1 that are used.
\r
50 // We are able to do this because the vertex list
\r
51 // gets sorted according to the collapse order.
\r
52 // The Map() routine takes a vertex number 'a' and the
\r
53 // maximum number of vertices 'mx' and returns the
\r
54 // appropriate vertex in the range 0 to mx-1.
\r
55 // When 'a' is greater than 'mx' the Map() routine
\r
56 // follows the chain of edge collapses until a vertex
\r
57 // within the limit is reached.
\r
58 // An example to make this clear: assume there is
\r
59 // a triangle with vertices 1, 3 and 12. But when
\r
60 // rendering the model we limit ourselves to 10 vertices.
\r
61 // In that case we find out how vertex 12 was removed
\r
62 // by the polygon reduction algorithm. i.e. which
\r
63 // edge was collapsed. Lets say that vertex 12 was collapsed
\r
64 // to vertex number 7. This number would have been stored
\r
65 // in the collapse_map array (i.e. collapse_map[12]==7).
\r
66 // Since vertex 7 is in range (less than max of 10) we
\r
67 // will want to render the triangle 1,3,7.
\r
68 // Pretend now that we want to limit ourselves to 5 vertices.
\r
69 // and vertex 7 was collapsed to vertex 3
\r
70 // (i.e. collapse_map[7]==3). Then triangle 1,3,12 would now be
\r
71 // triangle 1,3,3. i.e. this polygon was removed by the
\r
72 // progressive mesh polygon reduction algorithm by the time
\r
73 // it had gotten down to 5 vertices.
\r
74 // No need to draw a one dimensional polygon. :-)
\r
75 int Map(int a,int mx) {
\r
83 void DrawModelTriangles() {
\r
84 assert(collapse_map.num);
\r
87 for(i=0;i<tri.num;i++) {
\r
88 int p0= Map(tri[i].v[0],render_num);
\r
89 int p1= Map(tri[i].v[1],render_num);
\r
90 int p2= Map(tri[i].v[2],render_num);
\r
91 // note: serious optimization opportunity here,
\r
92 // by sorting the triangles the following "continue"
\r
93 // could have been made into a "break" statement.
\r
94 if(p0==p1 || p1==p2 || p2==p0) continue;
\r
96 // if we are not currenly morphing between 2 levels of detail
\r
97 // (i.e. if morph=1.0) then q0,q1, and q2 are not necessary.
\r
98 int q0= Map(p0,(int)(render_num*lodbase));
\r
99 int q1= Map(p1,(int)(render_num*lodbase));
\r
100 int q2= Map(p2,(int)(render_num*lodbase));
\r
102 v0 = vert[p0]*morph + vert[q0]*(1-morph);
\r
103 v1 = vert[p1]*morph + vert[q1]*(1-morph);
\r
104 v2 = vert[p2]*morph + vert[q2]*(1-morph);
\r
105 glBegin(GL_POLYGON);
\r
106 // the purpose of the demo is to show polygons
\r
107 // therefore just use 1 face normal (flat shading)
\r
108 Vector nrml = (v1-v0) * (v2-v1); // cross product
\r
109 if(0<magnitude(nrml)) {
\r
110 glNormal3fv(normalize(nrml));
\r
120 void PermuteVertices(List<int> &permutation) {
\r
121 // rearrange the vertex list
\r
122 List<Vector> temp_list;
\r
124 assert(permutation.num==vert.num);
\r
125 for(i=0;i<vert.num;i++) {
\r
126 temp_list.Add(vert[i]);
\r
128 for(i=0;i<vert.num;i++) {
\r
129 vert[permutation[i]]=temp_list[i];
\r
131 // update the changes in the entries in the triangle list
\r
132 for(i=0;i<tri.num;i++) {
\r
133 for(int j=0;j<3;j++) {
\r
134 tri[i].v[j] = permutation[tri[i].v[j]];
\r
139 void GetRabbitData(){
\r
140 // Copy the geometry from the arrays of data in rabdata.cpp into
\r
141 // the vert and tri lists which we send to the reduction routine
\r
143 for(i=0;i<RABBIT_VERTEX_NUM;i++) {
\r
144 float *vp=rabbit_vertices[i];
\r
145 vert.Add(Vector(vp[0],vp[1],vp[2]));
\r
147 for(i=0;i<RABBIT_TRIANGLE_NUM;i++) {
\r
149 td.v[0]=rabbit_triangles[i][0];
\r
150 td.v[1]=rabbit_triangles[i][1];
\r
151 td.v[2]=rabbit_triangles[i][2];
\r
154 render_num=vert.num; // by default lets use all the model to render
\r
159 List<int> permutation;
\r
161 ProgressiveMesh(vert,tri,collapse_map,permutation);
\r
162 PermuteVertices(permutation);
\r
163 model_position = Vector(0,0,-3);
\r
164 Quaternion yaw(Vector(0,1,0),-3.14f/4); // 45 degrees
\r
165 Quaternion pitch(Vector(1,0,0),3.14f/12); // 15 degrees
\r
166 model_orientation = pitch*yaw;
\r
169 void StatusDraw() {
\r
170 // Draw a slider type widget looking thing
\r
171 // to show portion of vertices being used
\r
172 float b = (float)render_num/(float)vert.num;
\r
173 float a = b*(lodbase );
\r
174 glDisable(GL_LIGHTING);
\r
175 glMatrixMode( GL_PROJECTION );
\r
178 glOrtho(-0.15,15,-0.1,1.1,-0.1,100);
\r
179 glMatrixMode( GL_MODELVIEW );
\r
183 glBegin(GL_POLYGON);
\r
190 glBegin(GL_POLYGON);
\r
193 glVertex2f(morph,a);
\r
194 glVertex2f(morph,b);
\r
197 glBegin(GL_POLYGON);
\r
199 glVertex2f(morph,a);
\r
202 glVertex2f(morph,b);
\r
204 glBegin(GL_POLYGON);
\r
212 glMatrixMode( GL_PROJECTION );
\r
214 glMatrixMode( GL_MODELVIEW );
\r
218 * The following is just a quick hack to animate
\r
219 * the object through various polygon reduced versions.
\r
221 struct keyframethings {
\r
222 float t; // timestamp
\r
223 float n; // portion of vertices used to start
\r
224 float dn; // rate of change in "n"
\r
225 float m; // morph value
\r
226 float dm; // rate of change in "m"
\r
243 void AnimateParameters() {
\r
244 static float time=0; // global time - used for animation
\r
246 if(time>=50) time=0; // repeat cycle every so many seconds
\r
248 while(time>keys[k+1].t) {
\r
251 float interp = (time-keys[k].t)/(keys[k+1].t-keys[k].t);
\r
252 render_num = vert.num*(keys[k].n + interp*keys[k].dn);
\r
253 morph = keys[k].m + interp*keys[k].dm;
\r
254 morph = (morph>1.0f) ? 1.0f : morph; // clamp value
\r
255 if(render_num>vert.num) render_num=vert.num;
\r
256 if(render_num<0 ) render_num=0;
\r
259 void RenderModel() {
\r
260 AnimateParameters();
\r
262 glEnable(GL_LIGHTING);
\r
263 glEnable(GL_LIGHT0);
\r
266 glTranslatef(model_position.x,model_position.y,model_position.z);
\r
267 // Rotate by quaternion: model_orientation
\r
268 Vector axis=model_orientation.axis();
\r
269 float angle=model_orientation.angle()*180.0f/3.14f;
\r
270 glRotatef(angle,axis.x,axis.y,axis.z);
\r
271 DrawModelTriangles();
\r
276 sprintf(buf,"Polys: %d Vertices: %d ",renderpolycount,render_num);
\r
278 sprintf(buf+strlen(buf),"<-> %d morph: %4.2f ",
\r
279 (int)(lodbase *render_num),morph);
\r
281 PostString(buf,0,-2,5);
\r