283 lines
8.3 KiB
C
283 lines
8.3 KiB
C
/*
|
|
* Polygon Reduction Demo by Stan Melax (c) 1998
|
|
* Permission to use any of this code wherever you want is granted..
|
|
* Although, please do acknowledge authorship if appropriate.
|
|
*
|
|
* This module initializes the bunny model data and calls
|
|
* the polygon reduction routine. At each frame the RenderModel()
|
|
* routine is called to draw the model. This module also
|
|
* animates the parameters (such as number of vertices to
|
|
* use) to show the model at various levels of detail.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <GL/gl.h>
|
|
#pragma warning(disable : 4244)
|
|
|
|
#include "vector.h"
|
|
#include "font.h"
|
|
#include "progmesh.h"
|
|
#include "rabdata.h"
|
|
|
|
extern float DeltaT; // change in time since last frame
|
|
int render_num; // number of vertices to draw with
|
|
float lodbase=0.5f; // the fraction of vertices used to morph toward
|
|
float morph=1.0f; // where to render between 2 levels of detail
|
|
List<Vector> vert; // global list of vertices
|
|
List<tridata> tri; // global list of triangles
|
|
List<int> collapse_map; // to which neighbor each vertex collapses
|
|
int renderpolycount=0; // polygons rendered in the current frame
|
|
Vector model_position; // position of bunny
|
|
Quaternion model_orientation; // orientation of bunny
|
|
|
|
// Note that the use of the Map() function and the collapse_map
|
|
// list isn't part of the polygon reduction algorithm.
|
|
// We just set up this system here in this module
|
|
// so that we could retrieve the model at any desired vertex count.
|
|
// Therefore if this part of the program confuses you, then
|
|
// dont worry about it. It might help to look over the progmesh.cpp
|
|
// module first.
|
|
|
|
// Map()
|
|
//
|
|
// When the model is rendered using a maximum of mx vertices
|
|
// then it is vertices 0 through mx-1 that are used.
|
|
// We are able to do this because the vertex list
|
|
// gets sorted according to the collapse order.
|
|
// The Map() routine takes a vertex number 'a' and the
|
|
// maximum number of vertices 'mx' and returns the
|
|
// appropriate vertex in the range 0 to mx-1.
|
|
// When 'a' is greater than 'mx' the Map() routine
|
|
// follows the chain of edge collapses until a vertex
|
|
// within the limit is reached.
|
|
// An example to make this clear: assume there is
|
|
// a triangle with vertices 1, 3 and 12. But when
|
|
// rendering the model we limit ourselves to 10 vertices.
|
|
// In that case we find out how vertex 12 was removed
|
|
// by the polygon reduction algorithm. i.e. which
|
|
// edge was collapsed. Lets say that vertex 12 was collapsed
|
|
// to vertex number 7. This number would have been stored
|
|
// in the collapse_map array (i.e. collapse_map[12]==7).
|
|
// Since vertex 7 is in range (less than max of 10) we
|
|
// will want to render the triangle 1,3,7.
|
|
// Pretend now that we want to limit ourselves to 5 vertices.
|
|
// and vertex 7 was collapsed to vertex 3
|
|
// (i.e. collapse_map[7]==3). Then triangle 1,3,12 would now be
|
|
// triangle 1,3,3. i.e. this polygon was removed by the
|
|
// progressive mesh polygon reduction algorithm by the time
|
|
// it had gotten down to 5 vertices.
|
|
// No need to draw a one dimensional polygon. :-)
|
|
int Map(int a,int mx) {
|
|
if(mx<=0) return 0;
|
|
while(a>=mx) {
|
|
a=collapse_map[a];
|
|
}
|
|
return a;
|
|
}
|
|
|
|
void DrawModelTriangles() {
|
|
assert(collapse_map.num);
|
|
renderpolycount=0;
|
|
int i=0;
|
|
for(i=0;i<tri.num;i++) {
|
|
int p0= Map(tri[i].v[0],render_num);
|
|
int p1= Map(tri[i].v[1],render_num);
|
|
int p2= Map(tri[i].v[2],render_num);
|
|
// note: serious optimization opportunity here,
|
|
// by sorting the triangles the following "continue"
|
|
// could have been made into a "break" statement.
|
|
if(p0==p1 || p1==p2 || p2==p0) continue;
|
|
renderpolycount++;
|
|
// if we are not currenly morphing between 2 levels of detail
|
|
// (i.e. if morph=1.0) then q0,q1, and q2 are not necessary.
|
|
int q0= Map(p0,(int)(render_num*lodbase));
|
|
int q1= Map(p1,(int)(render_num*lodbase));
|
|
int q2= Map(p2,(int)(render_num*lodbase));
|
|
Vector v0,v1,v2;
|
|
v0 = vert[p0]*morph + vert[q0]*(1-morph);
|
|
v1 = vert[p1]*morph + vert[q1]*(1-morph);
|
|
v2 = vert[p2]*morph + vert[q2]*(1-morph);
|
|
glBegin(GL_POLYGON);
|
|
// the purpose of the demo is to show polygons
|
|
// therefore just use 1 face normal (flat shading)
|
|
Vector nrml = (v1-v0) * (v2-v1); // cross product
|
|
if(0<magnitude(nrml)) {
|
|
glNormal3fv(normalize(nrml));
|
|
}
|
|
glVertex3fv(v0);
|
|
glVertex3fv(v1);
|
|
glVertex3fv(v2);
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
|
|
void PermuteVertices(List<int> &permutation) {
|
|
// rearrange the vertex list
|
|
List<Vector> temp_list;
|
|
int i;
|
|
assert(permutation.num==vert.num);
|
|
for(i=0;i<vert.num;i++) {
|
|
temp_list.Add(vert[i]);
|
|
}
|
|
for(i=0;i<vert.num;i++) {
|
|
vert[permutation[i]]=temp_list[i];
|
|
}
|
|
// update the changes in the entries in the triangle list
|
|
for(i=0;i<tri.num;i++) {
|
|
for(int j=0;j<3;j++) {
|
|
tri[i].v[j] = permutation[tri[i].v[j]];
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetRabbitData(){
|
|
// Copy the geometry from the arrays of data in rabdata.cpp into
|
|
// the vert and tri lists which we send to the reduction routine
|
|
int i;
|
|
for(i=0;i<RABBIT_VERTEX_NUM;i++) {
|
|
float *vp=rabbit_vertices[i];
|
|
vert.Add(Vector(vp[0],vp[1],vp[2]));
|
|
}
|
|
for(i=0;i<RABBIT_TRIANGLE_NUM;i++) {
|
|
tridata td;
|
|
td.v[0]=rabbit_triangles[i][0];
|
|
td.v[1]=rabbit_triangles[i][1];
|
|
td.v[2]=rabbit_triangles[i][2];
|
|
tri.Add(td);
|
|
}
|
|
render_num=vert.num; // by default lets use all the model to render
|
|
}
|
|
|
|
|
|
void InitModel() {
|
|
List<int> permutation;
|
|
GetRabbitData();
|
|
ProgressiveMesh(vert,tri,collapse_map,permutation);
|
|
PermuteVertices(permutation);
|
|
model_position = Vector(0,0,-3);
|
|
Quaternion yaw(Vector(0,1,0),-3.14f/4); // 45 degrees
|
|
Quaternion pitch(Vector(1,0,0),3.14f/12); // 15 degrees
|
|
model_orientation = pitch*yaw;
|
|
}
|
|
|
|
void StatusDraw() {
|
|
// Draw a slider type widget looking thing
|
|
// to show portion of vertices being used
|
|
float b = (float)render_num/(float)vert.num;
|
|
float a = b*(lodbase );
|
|
glDisable(GL_LIGHTING);
|
|
glMatrixMode( GL_PROJECTION );
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(-0.15,15,-0.1,1.1,-0.1,100);
|
|
glMatrixMode( GL_MODELVIEW );
|
|
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glBegin(GL_POLYGON);
|
|
glColor3f(1,0,0);
|
|
glVertex2f(0,0);
|
|
glVertex2f(1,0);
|
|
glVertex2f(1,a);
|
|
glVertex2f(0,a);
|
|
glEnd();
|
|
glBegin(GL_POLYGON);
|
|
glColor3f(1,0,0);
|
|
glVertex2f(0,a);
|
|
glVertex2f(morph,a);
|
|
glVertex2f(morph,b);
|
|
glVertex2f(0,b);
|
|
glEnd();
|
|
glBegin(GL_POLYGON);
|
|
glColor3f(0,0,1);
|
|
glVertex2f(morph,a);
|
|
glVertex2f(1,a);
|
|
glVertex2f(1,b);
|
|
glVertex2f(morph,b);
|
|
glEnd();
|
|
glBegin(GL_POLYGON);
|
|
glColor3f(0,0,1);
|
|
glVertex2f(0,b);
|
|
glVertex2f(1,b);
|
|
glVertex2f(1,1);
|
|
glVertex2f(0,1);
|
|
glEnd();
|
|
glPopMatrix();
|
|
glMatrixMode( GL_PROJECTION );
|
|
glPopMatrix();
|
|
glMatrixMode( GL_MODELVIEW );
|
|
}
|
|
|
|
/*
|
|
* The following is just a quick hack to animate
|
|
* the object through various polygon reduced versions.
|
|
*/
|
|
struct keyframethings {
|
|
float t; // timestamp
|
|
float n; // portion of vertices used to start
|
|
float dn; // rate of change in "n"
|
|
float m; // morph value
|
|
float dm; // rate of change in "m"
|
|
} keys[]={
|
|
{0 ,1 ,0 ,1, 0},
|
|
{2 ,1 ,-1,1, 0},
|
|
{10,0 ,1 ,1, 0},
|
|
{18,1 ,0 ,1, 0},
|
|
{20,1 ,0 ,1,-1},
|
|
{24,0.5 ,0 ,1, 0},
|
|
{26,0.5 ,0 ,1,-1},
|
|
{30,0.25,0 ,1, 0},
|
|
{32,0.25,0 ,1,-1},
|
|
{36,0.125,0,1, 0},
|
|
{38,0.25,0 ,0, 1},
|
|
{42,0.5 ,0 ,0, 1},
|
|
{46,1 ,0 ,0, 1},
|
|
{50,1 ,0 ,1, 0},
|
|
};
|
|
void AnimateParameters() {
|
|
static float time=0; // global time - used for animation
|
|
time+=DeltaT;
|
|
if(time>=50) time=0; // repeat cycle every so many seconds
|
|
int k=0;
|
|
while(time>keys[k+1].t) {
|
|
k++;
|
|
}
|
|
float interp = (time-keys[k].t)/(keys[k+1].t-keys[k].t);
|
|
render_num = vert.num*(keys[k].n + interp*keys[k].dn);
|
|
morph = keys[k].m + interp*keys[k].dm;
|
|
morph = (morph>1.0f) ? 1.0f : morph; // clamp value
|
|
if(render_num>vert.num) render_num=vert.num;
|
|
if(render_num<0 ) render_num=0;
|
|
}
|
|
|
|
void RenderModel() {
|
|
AnimateParameters();
|
|
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_LIGHT0);
|
|
glColor3f(1,1,1);
|
|
glPushMatrix();
|
|
glTranslatef(model_position.x,model_position.y,model_position.z);
|
|
// Rotate by quaternion: model_orientation
|
|
Vector axis=model_orientation.axis();
|
|
float angle=model_orientation.angle()*180.0f/3.14f;
|
|
glRotatef(angle,axis.x,axis.y,axis.z);
|
|
DrawModelTriangles();
|
|
StatusDraw();
|
|
glPopMatrix();
|
|
|
|
char buf[256];
|
|
sprintf(buf,"Polys: %d Vertices: %d ",renderpolycount,render_num);
|
|
if(morph<1.0) {
|
|
sprintf(buf+strlen(buf),"<-> %d morph: %4.2f ",
|
|
(int)(lodbase *render_num),morph);
|
|
}
|
|
PostString(buf,0,-2,5);
|
|
}
|
|
|