Handout 3 CS 116A Fall 2003
//SAMPLE Mesh code has NOT been compiled or tested.
//===========MESH.H======================
#include "vectormatrix.h"
#include <vector>
using namespace std
//################ Face ##################
class cFace
{
public:
vector<int> _vertexid; // the list of vertex indices
vector<int> _normalid; // the list of normal indices
cFace(){} // constructor
~cFace(){}// destructor
};
//###################### Mesh #######################
class cMesh
{
private:
vector<cVector> _vertex; // array of 3D vertices
vector<cVector> _normal; // array of normals
vector<cFace> _face; // array of face data
public:
cMesh(); // constructor
~cMesh(); // destructor
int facecount(){return _face.size();}
int vertcount(int faceindex){return _face[faceindex].size();}
cVector vertex(int faceindex, int i)
{return _vertex[_face[faceindex]._vertexid[i]];|
cVector normal(int faceindex, int i)
{return _normal[_face[faceindex]._normalid[i]];|
void draw(int drawflags);
};
//===========MESH.CPP======================
//Simple method.
void cMesh:: draw(int drawflags)
{
static int displayid = 0;
if (!displayid) //Only do this the first time
{
displayid = glGenLists(1);
glNewList(displayid, GL_COMPILE);
for(int j = 0; j < facecount(); j++) // draw each face
{
glBegin(GL_POLYGON);
for(int i = 0; i < face[j].vertcount(); i++) // for each one..
{
glNormal3d(normal(j, i).x(), normal(j, i).y(),
normal(j, i).z());
glVertex3d(vertex(j, i).x(), vertex(j, i).y(),
vertex(j, i).z());
}
glEnd();
}
glEndList();
} //End of the part you onlythe first time.
glCallList(displaylistid); //Always do this.
}
//Faster, more complicated Vector Array method
void cMesh:: draw(int drawflags)
{
/* Don't have time to write it. Uses calls to
glEnableClientState(GL_VERTEX_ARRAY);
allocate Real *vertices = new Real[...]
fill with lines like this: vertices[3+3*i] = ppolygon->getVertex(i).x();
allocate GLushort *quadindices = new GLushort[4*size];
fill with lines like this: quadindices[4*i] = 1 + i;
Use with lines like
::glBegin(GL_QUADS);
::glArrayElement(quadindices[quadindex++]);
::glArrayElement(quadindices[quadindex++]);
::glArrayElement(quadindices[quadindex++]);
::glArrayElement(quadindices[quadindex++]);
::glEnd();
You can do something similar for the normals.
*/
}
==================================ASSEMBLY.H CODE from MOLECULES1, has been tested.
class cAssembly
{
public:
static cAssembly* cAssembly::makeRandomAssembly(int maxdepth = MAXDEPTH);
/* makeRandomAssembly is a static (think "global") method that creates
random assembliess. Methods of this kind are called factory methods,
in that they create new objects for you. The meaning of the maxdepth
argument is that we guarantee not to return a nested cAssemblyComposite
with more than maxdepth levels. You use this method in your main program
by picking some value of MAXDEPTH maybe between 2 and 5 and writing a
line like
cAssembly *pnew = cAssembly::makeRandomAssembly(MAXDEPTH); */
protected:
//Geometry
Real _actualradius; /* Make this be the actual radius of the most
distant point form the origin. */
//Transformation
cVector _scale;
Real _spinangle;
cVector _spinaxis;
cVector _position;
//Motion
Real _spinrate;
public:
//Construtor, Destructor, Copy
cAssembly();
//Mutators
//Randomizer
virtual void randomize(); //Randomizes spinaxis and spinangle
//etcetera..
//Essential methods
virtual void animate(Real dt);
virtual void draw(int drawflags = 0){} //If drawflags is 1, we draw wireframe.
};
class cAssemblyComposite : public cAssembly
{
private:
vector <cAssembly*> _pchild;
public:
//Constructor, etc.
cAssemblyComposite(){} //Default constructor makes empty _pchild array
//Special methods
void add(cAssembly *pa);
void addRandomChildren(int maxdepth = cAssembly::MAXDEPTH);
//Overloads
virtual void fixActualRadius(); /* Try to adjust
_actualradius to include all the pieces. */
virtual int depth(); //Return 1 + max depth of children.
virtual void animate(Real dt);
virtual void draw(int drawflags = 0);
};
class cPolyhedron : public cAssembly
{ //as before
}
====================================ASSEMBLY.CPP
//========Good Stuff for cAssembly====================
void cAssembly::randomize()
{
_spinaxis = cVector::randomUnitVector();
_spinrate = cRandomizer::pinst()->randomReal(3.0*(2*PI), 20.0*(2*PI));
}
cAssembly* cAssembly::makeRandomAssembly(int depth)
{
cAssembly *pnewassembly;
if (depth >= 1)
{
pnewassembly = new cAssemblyComposite();
((cAssemblyComposite*)pnewassembly)-> //Have to cast so I can use method
addRandomChildren(depth); //Allow maxdepth more levels.
}
else
pnewassembly = new cPolyhedron();
pnewassembly->randomize(); /* Randomizes the spinaxis and spinrate, also,
if it's a polyhedron, randomizes the color and shape. */
pnewassembly->fixActualRadius();
return pnewassembly;
}
void cAssembly::animate(Real dt)
{
_position += dt*_velocity;
_spinangle += dt*_spinrate;
}
//===========Good cAssemblyComposite Stuff===============
void cAssemblyComposite::addRandomChildren(int maxdepth)
{
cAssembly *pnew;
int childcount = cRandomizer::pinst()->random(2, 3);
for (int i=0; i<childcount; i++)
{
pnew = cAssembly::makeRandomAssembly(maxdepth - 1);
#ifdef PLANETARY
if (i>0)
// I'll move the later guys out to be on periphery of the first guy.
{
cAssembly* pboss = _pchild[0];
cVector displacedir = cVector::randomUnitVector();
Real howfar = cRandomizer::pinst()->randomReal(0.5, 1.3);
pnew->setPosition(howfar * pboss->radius() * displacedir);
#ifdef SMALLCHILDREN
// I'll scale the later guys to be smaller.
Real shrinkfactor = cRandomizer::pinst()->randomReal(0.5, 0.8);
pnew->multiplyScale(shrinkfactor);
#endif //SMALLCHILDREN
}
#endif //PLANETARY
add(pnew);
}
}
void cAssemblyComposite::animate(Real dt)
{
cAssembly::animate(dt);
for (int i=0; i<_pchild.size(); i++)
_pchild[i]->animate(dt);
}
int cAssemblyComposite::depth()
{
int maxdepth = 0;
for (int i=0; i<_pchild.size(); i++)
if (maxdepth < _pchild[i]->depth())
maxdepth = _pchild[i]->depth();
return maxdepth + 1;
}
//===============new drawFlurry in DONUTS.CPP
void drawFlurry() //Uses the global dt param.
{
while (assemblyarray.size() < maxassemblycount)
{
cAssembly *pnewpoly;
pnewpoly = cAssembly::makeRandomAssembly(maxassemblycomplexity);
pnewpoly->setPosition(cVector(cRandomizer::pinst()->
randomReal(-0.5* xradius, 0.5 * xradius),
cRandomizer::pinst()->randomReal(-0.5* yradius, 0.5* yradius),
cRandomizer::pinst()->randomReal(0.0, zradius)));
assemblyarray.push_back(pnewpoly);
//push_back adds the pointer to the array.
}
for (int i=0; i<assemblyarray.size(); i++)
{
assemblyarray[i]->animate(dt);
if (fallflag)
assemblyarray[i]->translate(-FALLSPEED * dt * cVector::ZAXIS);
assemblyarray[i]->draw(wireflag | showconnectorflag | showradiusflag);
if (assemblyarray[i]->position().z() <
-zradius + assemblyarray[i]->radius())
{
delete assemblyarray[i]; //Delete it before removing from the deque.
assemblyarray.erase(assemblyarray.begin()+i); //Erase when you hit bottom.
}
}
}
==========MOLECULE 1 CONTROLS===========
' ': Space singlesteps if animation flag is turned off.
'a': Toggles the running animation.
'b': Toggles wireframe balls around the assemblies. The colors of
the balls indicate the "depth" of the assmbly, that is, how many
levels of subassemblies it has.
'c': Toggles wireframe lines connecting the first member of an assembly
to the other. Color = depth as in 'b'.
'd': Decrements maximum depth or complexity of assemblies. The minumum
depth of 0 is a polyhedron.
D': Increments depth.
'f': Toggles the falling-towards-the-viwer motion.
'n': Decrements the number of assemblies on screen.
'N': Increments the number of assemblies on screen.
'q': Quit
'r': Randomize the assemblies on screen.
'w': Toggle a wireframe view.
'z': Zoom in.
'Z': Zoom out.
'2': Use a 2d orthogonal projection.
'3': Use a 3d perspective projection.