How do I display/draw a .ply object in OpenGL? - c++

I'm trying to make OpenGL draw the figure that I'm loading with OPENFILENAME. What I've got right now is: I can display the comments, vertex, how many faces, etc., but I cannot draw the figure and I'm not sure how to do it. I can draw other predetermined figures, but not the ones I'm trying to open.
This is where I'm initializing everything:
case WM_CREATE:
hDC = GetDC(hWnd);
hRC=wglCreateContext(hDC);
wglMakeCurrent(hDC,hRC);
g_hwndDlg = CreateDialog(hInst,MAKEINTRESOURCE(IDD_DIALOG1),hWnd,DialogProc);
Figure = new DrawFigure();
initGL();
break;
This is where I find out what the element I'm opening has:
/* go through each kind of element that we learned is in the file */
/* and read them */
for (i = 0; i < nelems; i++) {
/* get the description of the first element */
elem_name = elist[i];
plist = ply_get_element_description (ply, elem_name, &num_elems, &nprops);
int el=sprintf(szFile,"element %s %d\n", elem_name, num_elems);
/* print the name of the element, for debugging */
TextOut(hDC,150,0+i*20,szFile,el);
/* if we're on vertex elements, read them in */
if (equal_strings ("vertex", elem_name)) {
/* create a vertex list to hold all the vertices */
vlist = (Vertex **) malloc (sizeof (Vertex *) * num_elems);
/* set up for getting vertex elements */
ply_get_property (ply, elem_name, &vert_props[0]);
ply_get_property (ply, elem_name, &vert_props[1]);
ply_get_property (ply, elem_name, &vert_props[2]);
/* grab all the vertex elements */
for (j = 0; j < num_elems; j++) {
int move=10;
/* grab and element from the file */
vlist[j] = (Vertex *) malloc (sizeof (Vertex));
ply_get_element (ply, (void *) vlist[j]);
int vert=sprintf(szFile,"vertex: %g %g %g", vlist[j]->x, vlist[j]->y, vlist[j]->z);
/* print out vertex x,y,z for debugging */
TextOut(hDC,600,move+j*20,szFile,vert);
Figure->Parameters(vlist[j]->x, vlist[j]->y, vlist[j]->z);
}
}
And this is where the class Figure is, where I'm suppossed to draw everything:
Figure::Figure(){
}
void Figure::Parameters(float x,float y,float z)
{
this->x1=x;
this->y1=y;
this->z1=z;
}
void Figure::Draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0,0.0,4.0,0.0,0.0,0.0,0.0,1.0,0.0);
glBegin(GL_TRIANGLES);
glNormal3f(x1,y1,z1);
glVertex3f(x1,y1,z1);
glEnd();
}
x1,y1,z1 are declared in Figure.h
I tried to explain myself the best I could; if you think it still needs more explanation please tell me and I will try to explain it in a different way
Yeah, I forgot to explain I guess the figure I'm trying to draw...well i don't know which figure it would be because I'm using OPENFILENAME to open 1 random figure and draw it i used triangles because i thought that with triangles i could draw anything and also i tried in the class Parameters ask for the number of vertex I'm dealing with and making a "for" in the class Draw but it didn't work

You only specify one vertex between your begin/end.. you need at least 3 to specify a triangle. And many more if you want a whole buncha triangles. You need something more along the lines of this:
void Figure::Parameters(float x, float y, float z)
{
m_vertices.push_back(myVertex(x, y, z));
}
void Figure::Draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0,0.0,4.0,0.0,0.0,0.0,0.0,1.0,0.0);
glBegin(GL_TRIANGLES);
assert(m_vertices.size() % 3 == 0); // since we're drawing triangles
for(size_t i=0; i<m_vertices.size(); i++)
{
glNormal3f(m_vertices[i].x,m_vertices[i].y,m_vertices[i].z);
glVertex3f(m_vertices[i].x,m_vertices[i].y,m_vertices[i].z);
}
glEnd();
}

Related

c++ Opengl handle elements drawn

I am currently working on a game and I want to know if there is any way of handling with the elements i am drawing . For example : if i draw in a loop 100 cubes , how can i show / hide the cube number 15 or 63 or n ... I thought that initializing elements in a list would work , but i didn't find any property of it that could help.
GLuint cube;
cube = glGenLists(1);
glNewList(cube,GL_COMPILE);
for(int i = -30; i < 3; i++) {
for(int j = -30; j < 3; j++) {
glPushMatrix();
glTranslatef(i*2.0,0,j * 2.0);
Dcube();
glPopMatrix();
}
}
glEndList();
//something like : glDeleteList(cube); but that only works with entire list not with individual objects..
You have a display list, very good. So now you're back to using your regular language primitives to simply call that function.
std::array<bool, 100> cubes;
std::fill(cubes.begin(), cubes.end(), true);
cubes[15] = false;
cubes[63] = false;
for (bool drawCube : cubes) {
if (drawCube) {
// move a bit, perhaps using glTranslate
glCallList(cube);
}
}
OpenGL isn't your statekeeper. It just draws what you tell it to, you're responsible for keeping your objects.

OpenGL:How to make a "Polyman's" mouth open

I'm writing a program that has a polygon "man" walk from one side of the screen to the middle, open his mouth, jump up and do a flip, land, close his mouth, and walk left off the screen. I am a bit confused on how to make the lad's mouth open. I was thinking I could create a triangle of appropriate size the same color as the background and slowly translate it to where his mouth would be. What steps would I need to go through to make this possible, and where would I put the code to do so?
#include<Windows.h>
#include<GL/glut.h>
#include<stdlib.h>
#include<math.h>
#include<conio.h>
#include<stdio.h>
#include<iostream>
#include<iomanip>
#include<gl/glut.h>
using namespace std;
//***********************GLOBAL VALUES*********************************************
float theta=00.0; //global angular value for rotation
float scale1=1.0; //global scaling value
float dx=7.0,dy=-3.0;
int frame = 1;
void init(void); //This is a function to initialize the window clear color
void RenderScene(void); //This a function to draw polyman in an opened window
void loadicon(float[],float[],float[],float[], float[], float[]); //Load the polyman icon
void drawicon(float[],float[],float[],float[], float[], float[]); //Draw the icon the two first float for the square and the others for the line
void settrans(float[][3],float,float,float); //Sets the transformation matrix for desired scale, rotation, new pos
float xprime(float,float,float[][3]); //Calculates x' from x and transform
float yprime(float,float,float[][3]); //Calculates y' from y and transform
void transform(float[],float[],float[],float[], float[], float[], float[][3],float[],float[],float[],float[], float[], float[]); //performs the transformation on the icon pattern
void myidle(void);
void SetupRC(void); //Sets up the clear color
void TimerFunction(int); //This call back function is called each 30ms and changes the location, scale and rotation
//***********************MAIN PROGRAM**********************************************
int main(int argc, char** argv)
{
//Set up window title
char header[]="Polyman's journey";
glutInit(&argc,argv);
//Set up the display mode with two buffers and RGB colors
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
//Initialize window size and position
glutInitWindowSize(560,440);
glutInitWindowPosition(140,20);
//Initialize background color of the window
SetupRC();
//Open and label window
glutCreateWindow(header);
glutDisplayFunc(RenderScene);
glutTimerFunc(30,TimerFunction, 1); //Call the TimerFunction each 30s
//Now draw the scene
glutMainLoop();
return 0;
}
//*************************RenderScene Function*************************************
void RenderScene(void)
{
float xdel=0.25;
float px[7],py[7],plx[4],ply[4], pl2x[4], pl2y[4];// These variables hold the pattern for the icon square plus line
float pxp[7],pyp[7],plxp[4],plyp[4], pl2xp[4], pl2yp[4],t[3][3]; //These varables hold the pattern after transformation, t is the transformation matrix
//clear the window with the current background color
cout<<"in RenderScene"<<endl;
//set the current drawing color to white
glColor3f(1.0,1.0,1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//set the viewport to the window dimensions
glViewport(0,0,560,440);
//Establish the clipping volumn in user units, first clear all the translation matrices
glOrtho(-7.0,7.0,-7.0,7.0,1.0,-1.0);
loadicon(px,py,plx,ply, pl2x, pl2y);
//draw the icon untransformed
settrans(t,scale1,dx,dy);
transform(pxp,pyp,plxp,plyp,pl2xp,pl2yp, t,px,py,plx,ply, pl2x, pl2y);
//clear the window with the background color
glClear(GL_COLOR_BUFFER_BIT);
//set the current drawing color to white
glColor3f(1.0,1.0,1.0);
//now draw the figure
drawicon(pxp,pyp,plxp,plyp,pl2xp, pl2yp);
glEnd();
glutSwapBuffers();
return;
}//end of render scene
//************************LOAD ICON FUNCTION***********************************
void loadicon(float px[],float py[],float plx[],float ply[],float pl2x[],float pl2y[]) //Loads the polyman
{
//Set the coordinates of the square
px[0]=-0.625 ; py[0]=0.625 ;
px[1]=0.625 ; py[1]=0.625 ;
px[2]=1.0 ; py[2]=0.0 ;
px[3]=0.625 ; py[3]=-0.625 ;
px[4]=-0.625 ; py[4]=-0.625 ;
px[5]= -1.0 ; py[5]= 0.0 ;
px[6]=-0.625 ; py[6]=0.625 ;
//set the right foot
plx[0]= 0.25 ; ply[0]= -0.625 ;
plx[1]= 0.25 ; ply[1]= -0.875 ;
plx[2]= 0.0 ; ply[2]= -0.875 ;
plx[3] = 0.25 ; ply[3] = -0.875;
//set the left foot
pl2x[0]= -0.125 ; pl2y[0]= -0.375 ;
pl2x[1]= -0.125 ; pl2y[1]= -0.875 ;
pl2x[2]= -0.375 ; pl2y[2]= -0.875 ;
pl2x[3]= -0.125 ; pl2y[3]= -0.875 ;
return;
} //end of loadicon
//************************FUNCTION DRAWICON***********************************
void drawicon(float pxp[],float pyp[],float plxp[],float plyp[], float pl2xp[], float pl2yp[])
{
//draw the square icon at the transformed position
int i;
cout<<"in drawicon"<<endl;
glBegin(GL_LINE_STRIP);
//move to first point in the icon
glVertex2f(pxp[0],pyp[0]);
//now draw the rest of the box
for(i = 1; i <= 6; i++)
{
glVertex2f(pxp[i],pyp[i]);
}
glEnd();
//now draw the line
glBegin(GL_LINES);
glVertex2f(plxp[0],plyp[0]);
for (i=1; i <=3; i++)
{
glVertex2f(plxp[i],plyp[i]);
}//glVertex2f(plxp[2],plyp[2]);
glEnd();
glBegin(GL_LINES);
glVertex2f(pl2xp[0], pl2yp[0]);
for (i=1; i <=3; i++)
{
glVertex2f(pl2xp[i], pl2yp[i]);
}
glEnd();
//now fill the rectangle which is made by half of the square
//set the shading color to green
glColor3f(0.0,1.0,0.0);
glShadeModel(GL_FLAT);
//redraw the polygon
glBegin(GL_POLYGON);
//Firts point is where the line intersects the top of the square
glVertex2f(pxp[0], pyp[0]);
//rigth corner upper
glVertex2f(pxp[1],pyp[1]);
//right corner lower
glVertex2f(pxp[2],pyp[2]);
//left intersect
glVertex2f(pxp[3],pyp[3]);
glVertex2f(pxp[4],pyp[4]);
glVertex2f(pxp[5],pyp[5]);
glVertex2f(pxp[6],pyp[6]);
return;
} //end of draw icon
//************************FUNCTION SETTRANS***********************************
void settrans(float t[][3],float scale1,float dx,float dy)
{
cout<<"in settrans"<<endl;
int i,j;
float ts,ct,st;
double theta1;
//setup identity matrix
for(i=0;i<=2;i++)
{
for(j=0;j<=2;j++)
{
t[i][j]=0.0;
if(i==j) t[i][j]=1.0;
}
}
//set scale parameters
if(scale1!=-9.0)
{
t[0][0]=scale1;
t[1][1]=scale1;
}
if(theta!=-9.0)
{
theta1=(3.1416/180.0)*theta;
ct=cos(theta1);
st=sin(theta1);
ts=t[0][0];
t[0][0]=ts*ct;
t[0][1]=ts*st;
ts=t[1][1];
t[1][0]=-ts*st;
t[1][1]=ts*ct;
}
//translate the figure
if((dx+dy) != -18.0)
{
t[2][0]=dx;
t[2][1]=dy;
}
return;
}//end of settrans
//************************FUNCTION XPRIME***********************************
float xprime(float x1,float y1, float t[][3])
{
//this function pultiples the x vector by the transformation matrix
float xp1;
xp1=x1*t[0][0]+y1*t[1][0]+t[2][0];
return xp1;
}
//************************FUNCTION YPRIME***********************************
float yprime(float x1,float y1, float t[][3])
{
//this function pultiples the y vector by the transformation matrix
float yp1;
yp1=x1*t[0][1]+y1*t[1][1]+t[2][1];
return yp1;
}
//************************FUNCTION TRANSFORM***********************************
void transform(float pxp[],float pyp[],float plxp[],float plyp[],float pl2xp[],float pl2yp[],float t[][3],float px[],float py[],float plx[],float ply[], float pl2x[], float pl2y[])
{
int i;
cout<<"in transform"<<endl;
//transform the figure
for(i=0;i<=6;i++)
{
pxp[i] = xprime(px[i],py[i],t);
pyp[i] = yprime(px[i],py[i],t);
}
//transform the line
for(i=0;i<=3;i++)
{
plxp[i] = xprime(plx[i],ply[i],t);
plyp[i] = yprime(plx[i],ply[i],t);
}
for(i=0;i<=3;i++)
{
pl2xp[i] = xprime(pl2x[i],pl2y[i],t);
pl2yp[i] = yprime(pl2x[i],pl2y[i],t);
}
return;
}//end of transform
//************************ FUNCTION SetupRC***********************************
void SetupRC(void)
{
//sets the clear color of an open window and clears the open window
//set clear color to green
glClearColor(0.0,1.0,0.0,1.0);
return;
}//end of setupRC
//************************ FUNCTION Timer***********************************
void TimerFunction(int value)
{
//this call back function is called each 30ms and changes the location, scale and rotation
static float swc=0.1,sdx=0.1,sdy=0.1;
switch(frame)
{
case 1:
//theta+=5.0;
dx-=0.15;
if(dx<=0.0)
{
dx=0.0;
frame=2;
}
break;
case 2:
dy+=0.2;
if(dy>5.0)
{
dy=5.0;
frame=3;
}
break;
case 3:
theta+=5.0;
if(theta>360.0)
{
frame=4;
theta=0.0;
scale1=1.0;
}
break;
case 4:
dy-=0.2;
if(dy<=-3.0)
{
dy=-3.0;
frame=5;
}
break;
case 5:
dx-=0.15;
//theta+=5.0;
if(dx<=-8.0) dx=-8.0;
break;
}
//redraw the scene with new coordinate
glutPostRedisplay();
glutTimerFunc(33,TimerFunction,1);
}
Also, we aren't allowed to use any of the built in translate, rotate, or scale functions that OpenGL provides since it's our first assignment. Thanks for all the help, I really appreciate it.

Tessellation - saving data

I am using tessellation in order to transfer non triangled polygons to triangle polygons.
I am trying to save the data in variable and run the tessellation code once while saving the data. The code use the saved data in order to draw it in draw function.
I am trying the draw a star polygon, The problem is that I can see some triangles but not a star. Why when I save the data the drawing go wrong?
Here is the initialize code:
#define callback void(CALLBACK*)()
void Init()
{
GLdouble star[5][6] = /* star data, The data is 100% perfect */
glColor3f(0.0f, 1.0f, 0.0f);
GLUtesselator *pTess = gluNewTess();
indexNum = 0;
gluTessCallback(pTess, GLU_TESS_BEGIN, (callback)glDrowMode);
gluTessCallback(pTess, GLU_TESS_VERTEX, (callback)saveData);
gluTessCallback(pTess, GLU_TESS_ERROR, (callback)tessError);
gluTessCallback(pTess, GLU_TESS_COMBINE, (callback) scbCombine);
gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
gluTessBeginPolygon(pTess, NULL);
gluTessBeginContour(pTess);
for(int i = 0; i < 5; i++)
gluTessVertex(pTess, star[i], star[i]);
gluTessEndContour(pTess);
gluTessEndPolygon(pTess);
gluDeleteTess(pTess);
}
The saving data code and saving the drawing mode:
struct vecStruct
{
GLdouble *vertex, *color;
};
vecStruct vec[16];
int indexNum = 0;
void CALLBACK saveData(const GLvoid *ptr)
{
const GLdouble *data = (const GLdouble*)ptr;
vec[indexNum].vertex = new GLdouble[3];
vec[indexNum].color = new GLdouble[3];
vec[indexNum].vertex[0] = data[0];
vec[indexNum].vertex[1] = data[1];
vec[indexNum].vertex[2] = data[2];
vec[indexNum].color[0] = data[3];
vec[indexNum].color[1] = data[4];
vec[indexNum].color[2] = data[5];
indexNum++;
}
GLenum drawMode;
void CALLBACK glDrowMode(GLenum where)
{
drawMode = where;
}
And last the drow function:
void vboDraw()
{
glBegin(drawMode);
for (int i = 0; i < indexNum; i++)
{
glColor3dv(vec[i].color);
glVertex3dv(vec[i].vertex);
}
glEnd();
}
As I have said I should see star:
But what I can see is some triangles:
What is wrong with the code?
Why can not I save the data for doing the tessellation code only once?

OpenGL Picking on Selection Mode

I know that OpenGL selection mode is deprecated and never was HW accelerated, except on a few SGI boxes and 3DLabs GPUs.But i can't get rid of it (not my code).Below its the C++ code:
void GLWidget::Selection(int x,int y) // This Is Where Selection Is Done
{
GLint viewport[4];
glSelectBuffer(BUFSIZE,selectBuf);
glRenderMode(GL_SELECT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(x,viewport[3]-y,5,5,viewport); //defining the picking matrix
gluPerspective(fov,ratio,0.1f,1000);
glMatrixMode(GL_MODELVIEW);
glInitNames();
glPushName(1); //Pushing names on the stack
glutSolidTorus(1, 2, 55, 55); //Some draw in GL_SELECT mode
glTranslatef(5.0f,1,5.0f);
glPopName();
glPushName(2);
glutSolidTorus(1, 2, 55, 55);
glTranslatef(5.0f,1,5.0f);
glPopName();
glPushName(3);
glutSolidTorus(RADIUS1, RADIUS2, complex1, complex2); //public members
glTranslatef(5.0f,1,5.0f);
glPopName();
int hits;
// restoring the original projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
// returning to normal rendering mode
hits = glRenderMode(GL_RENDER);
// if there are hits process them
if (hits != 0){
qDebug() << "Found " << hits << " hit(s)";
processHits(hits,selectBuf);
}
}
This is the processHits method
void GLWidget::processHits (GLint hits, GLuint buffer[]) //Some prints
{
unsigned int i, j;
GLuint names, *ptr, minZ,*ptrNames, numberOfNames;
ptr = (GLuint *) buffer;
minZ = 0xffffffff;
for (i = 0; i < hits; i++) {
names = *ptr;
ptr++;
if (*ptr < minZ) {
numberOfNames = names;
minZ = *ptr;
ptrNames = ptr+2;
}
ptr += names+2;
}
qDebug() << "Nearest: ";
ptr = ptrNames;
for (j = 0; j < numberOfNames; j++,ptr++) {
qDebug() << *ptr ;
}
}
Selection() is invoked by using an *event (GLWidget derives from QGLWidget (QT 4.8)).
So,only when i click the right mousebutton I ""draw"" objects in the buffer and push their names on the stack.
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
if (event->buttons() & GLUT_RIGHT_BUTTON){
Selection(event->x(),event->y());
}
}
While the paintGL() method is
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glViewport (0, 0, w_screen, h_screen);
gluLookAt(objCamera->mPos.x, objCamera->mPos.y, objCamera->mPos.z,
0, objCamera->mView.y, 0,
objCamera->mUp.x, objCamera->mUp.y, objCamera->mUp.z);
glutSolidTorus(1, 2, 55, 55); //draw some objects
glTranslatef(5.0f,1,5.0f);
glutSolidTorus(1, 2, 55, 55);
glTranslatef(5.0f,1,5.0f);
glutSolidTorus(RADIUS1, RADIUS2, complex1, complex2);
glTranslatef(5.0f,1,5.0f);
glTranslatef(-15.0f,-3,-15.0f);
}
At this time,with this code, I can select an object and retrieve its ID,and if there are more of them on the same xy coordinates i can retrieve the nearest one (by ID).
So,now I've got 3 objects with 3 different IDs (1-2-3).
The one with ID=3 has non fixed size.My question is:how to use the buffer to retrieve the third torus and change its size modifying RADIUS1, RADIUS2, complex1, complex2?
Could someone of you write a small example?
Have I,when there is a hit,simply use the name of the hit on the stack (given with glPushName),that must refers in some way to an object (maybe with a string public member wich contains the name),so i can change its properties?
You are going to have to store the properties of each object yourself. When you push a name onto the name stack, all you're doing is adding extra data to each fragment so you can effectively identify where it came from later.
You'll probably want to create a structure like { float rad1, rad2, complex1, complex2; } to store the values for each torus. Once you know the ID of the object selected, go into your array of said struct, and modify the values for the appropriate object. Each time you draw your scene, just run through this array of structs.

OpenGL skeleton animation

I am trying to add animation to my program.
I have human model created in Blender with skeletal animation, and I can skip through the keyframes to see the model walking.
Now I've exported the model to an XML (Ogre3D) format, and in this XML file I can see the rotation, translation and scale assigned to each bone at a specific time (t=0.00000, t=0.00040, ... etc.)
What I've done is found which vertices are assigned each bone. Now I'm assuming all I need to do is apply the transformations defined for the bone to each one of these vertices. Is this the correct approach?
In my OpenGL draw() function (rough pseudo-code):
for (Bone b : bones){
gl.glLoadIdentity();
List<Vertex> v= b.getVertices();
rotation = b.getRotation();
translation = b.getTranslation();
scale = b.getScale();
gl.glTranslatef(translation);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(v);
}
Vertices are usually affected by more than one bone -- it sounds like you're after linear blend skinning. My code's in C++ unfortunately, but hopefully it'll give you the idea:
void Submesh::skin(const Skeleton_CPtr& skeleton)
{
/*
Linear Blend Skinning Algorithm:
P = (\sum_i w_i * M_i * M_{0,i}^{-1}) * P_0 / (sum i w_i)
Each M_{0,i}^{-1} matrix gets P_0 (the rest vertex) into its corresponding bone's coordinate frame.
We construct matrices M_n * M_{0,n}^-1 for each n in advance to avoid repeating calculations.
I refer to these in the code as the 'skinning matrices'.
*/
BoneHierarchy_CPtr boneHierarchy = skeleton->bone_hierarchy();
ConfiguredPose_CPtr pose = skeleton->get_pose();
int boneCount = boneHierarchy->bone_count();
// Construct the skinning matrices.
std::vector<RBTMatrix_CPtr> skinningMatrices(boneCount);
for(int i=0; i<boneCount; ++i)
{
skinningMatrices[i] = pose->bones(i)->absolute_matrix() * skeleton->to_bone_matrix(i);
}
// Build the vertex array.
RBTMatrix_Ptr m = RBTMatrix::zeros(); // used as an accumulator for \sum_i w_i * M_i * M_{0,i}^{-1}
int vertCount = static_cast<int>(m_vertices.size());
for(int i=0, offset=0; i<vertCount; ++i, offset+=3)
{
const Vector3d& p0 = m_vertices[i].position();
const std::vector<BoneWeight>& boneWeights = m_vertices[i].bone_weights();
int boneWeightCount = static_cast<int>(boneWeights.size());
Vector3d p;
if(boneWeightCount != 0)
{
double boneWeightSum = 0;
for(int j=0; j<boneWeightCount; ++j)
{
int boneIndex = boneWeights[j].bone_index();
double boneWeight = boneWeights[j].weight();
boneWeightSum += boneWeight;
m->add_scaled(skinningMatrices[boneIndex], boneWeight);
}
// Note: This is effectively p = m*p0 (if we think of p0 as (p0.x, p0.y, p0.z, 1)).
p = m->apply_to_point(p0);
p /= boneWeightSum;
// Reset the accumulator matrix ready for the next vertex.
m->reset_to_zeros();
}
else
{
// If this vertex is unaffected by the armature (i.e. no bone weights have been assigned to it),
// use its rest position as its real position (it's the best we can do).
p = p0;
}
m_vertArray[offset] = p.x;
m_vertArray[offset+1] = p.y;
m_vertArray[offset+2] = p.z;
}
}
void Submesh::render() const
{
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_DOUBLE, 0, &m_vertArray[0]);
if(m_material->uses_texcoords())
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_DOUBLE, 0, &m_texCoordArray[0]);
}
m_material->apply();
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_vertIndices.size()), GL_UNSIGNED_INT, &m_vertIndices[0]);
glPopAttrib();
glPopClientAttrib();
}
Note in passing that real-world implementations usually do this sort of thing on the GPU to the best of my knowledge.
Your code assumes that each bone has an independent transformation matrix (you reset your matrix at the start of each loop iteration). But in reality, bones form a hierarchical structure that you must preserve when you do your rendering. Consider that when your upper arm rotates your forearm rotates along, because it is attached. The forearm may have its own rotation, but that is applied after it is rotated with the upper arm.
The rendering of the skeleton then is done recursively. Here is some pseudo-code:
function renderBone(Bone b) {
setupTransformMatrix(b);
draw(b);
foreach c in b.getChildren()
renderBone(c);
}
main() {
gl.glLoadIdentity();
renderBone(rootBone);
}
I hope this helps.