opengl vbo texture
I am still a beginner in vbo and I want to render a collada model. After taking all the needed info from the cae file about the vertices, normals and textures and indices for the vertices, normals and textures respectively, then i placed them in memory as a Model object. the thing that remained is how to render this object using vbo with the textures. I managed to render the object successfully by taking the vertices and indices then applying drawlelement for that in case of vbo render. But when I try to render textures it fails while rendering them using the display list works correctly thats because I am passing through every triangle and applying the vertex and texcoord correctly according to the vertex index and texture index. So how can i apply the texture indices found in the object in vbo in order for the texture to be rendered correctly?
This is the code for the project:
#include "bmp.h"
struct mdl_vert
{
float x,y,z;
};
struct mdl_normal
{
float x,y,z;
};
struct mdl_texCoord
{
float u,v;
};
struct mdl_capacity
{
int vertsSize,indicesSize,normalsSize,texCoordsSize;
};
struct mdl_indices
{
int *vertIndices;
int *normalIndices;
int *texIndices;
};
class Group
{
public:
mdl_capacity capacity;
mdl_vert *verts;
mdl_normal *normals;
mdl_texCoord *texCoords;
mdl_indices indices;
char grp_name[64];
Group()
{
capacity.texCoordsSize=0;
capacity.normalsSize=0;
capacity.indicesSize=0;
capacity.vertsSize=0;
verts=NULL;
normals=NULL;
texCoords=NULL;
indices.vertIndices=NULL;
indices.normalIndices=NULL;
indices.texIndices=NULL;
for(int i=0;i<64;i++)
grp_name[i]='\0';
}
};
class Model
{
public:
char tex_name[512];
std::string Name;
Group **groups;
int groupsSize;
}
#define VBO_BUFFER_SIZE 4
struct GroupObject
{
GLuint *buffer;
};
class ModelObject
{
private:
GLuint displayList;
GroupObject *groupBuffer;
bool isLoaded;
GLuint tex;
float ang,iniRotX,iniRotY,iniRotZ;
Model *mdl;
bool Hide;
AUX_RGBImageRec* m_pTextureImage; // Heightmap Data
public:
ModelObject(Model* mdl)
{
this->mdl=mdl;
Redraw();
}
void Redraw()
{
Hide=false;
ang=-0;
iniRotX=1;
iniRotY=0;
iniRotZ=0;
if(displayList!=-1)
displayList=-1;
groupBuffer=NULL;
}
void BuildTexture(int i)
{
if(mdl->groups[i]->capacity.texCoordsSize>0)
{
try
{
m_pTextureImage = auxDIBImageLoad( mdl->tex_name ); // Utilize GLaux's Bitmap Load Routine
glGenTextures( 1, &tex ); // Get An Open ID
glBindTexture( GL_TEXTURE_2D, tex ); // Bind The Texture
glTexImage2D( GL_TEXTURE_2D, 0, 3, m_pTextureImage->sizeX, m_pTextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, m_pTextureImage->data );
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
// Free The Texture Data
if( m_pTextureImage )
{
if( m_pTextureImage->data )
free( m_pTextureImage->data );
free( m_pTextureImage );
}
}
catch(std::exception &ex)
{
}
}
}
void BuildVBO()
{
int index=0;
groupBuffer=new GroupObject[mdl->groupsSize];
for(int i=0;i<mdl->groupsSize;i++)
{
BuildTexture(i);
groupBuffer[i].buffer=new GLuint[VBO_BUFFER_SIZE];
// Generate And Bind The Vertex Buf开发者_如何转开发fer
glGenBuffersARB( 1, &groupBuffer[i].buffer[0] ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[0] ); // Bind The Buffer
glBufferDataARB( GL_ARRAY_BUFFER_ARB,mdl->groups[i]->capacity.vertsSize*sizeof(mdl_vert), mdl->groups[i]->verts, GL_STATIC_DRAW_ARB );
// Generate And Bind The Normal Buffer
glGenBuffersARB( 1, &groupBuffer[i].buffer[1] ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[1] ); // Bind The Buffer
glBufferDataARB( GL_ARRAY_BUFFER_ARB,mdl->groups[i]->capacity.normalsSize*sizeof(mdl_normal), mdl->groups[i]->normals, GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Buffer
glGenBuffersARB( 1, &groupBuffer[i].buffer[2] ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[2] ); // Bind The Buffer
glBufferDataARB( GL_ARRAY_BUFFER_ARB,mdl->groups[i]->capacity.texCoordsSize*sizeof(mdl_texCoord), mdl->groups[i]->texCoords, GL_STATIC_DRAW_ARB );
// Generate And Bind The Index Buffer
glGenBuffersARB(1, &groupBuffer[i].buffer[3]);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[3]);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mdl->groups[i]->capacity.indicesSize*sizeof(unsigned int), mdl->groups[i]->indices.vertIndices, GL_STATIC_DRAW_ARB);
}
}
void render()
{
if(!Hide)
{
if(Drawer.vboSupported())
{
if(groupBuffer==NULL)
BuildVBO();
glRotatef(ang,iniRotX,iniRotY,iniRotZ);
for(int i=0;i<mdl->groupsSize;i++)
{
// Enable Pointers
glEnableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glEnableClientState( GL_NORMAL_ARRAY ); // Enable Normal Arrays
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[0] );
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[1] );
glNormalPointer(GL_FLOAT,0, BUFFER_OFFSET(0));
glClientActiveTexture(GL_TEXTURE0);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, groupBuffer[i].buffer[2] );
glTexCoordPointer(2,GL_FLOAT,0,BUFFER_OFFSET(0));
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER, groupBuffer[i].buffer[3]);
glIndexPointer(GL_UNSIGNED_INT,0,BUFFER_OFFSET(0));
glDrawElements( GL_TRIANGLES, mdl->groups[i]->capacity.indicesSize,GL_UNSIGNED_INT, 0); // Draw All Of The Triangles At Once
// Disable Pointers
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_NORMAL_ARRAY ); // Enable Normal Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Enable Texture Coord Arrays
}
glRotatef(-ang,iniRotX,iniRotY,iniRotZ);
}
else
{
if(this->displayList==-1)
{
displayList = glGenLists(1);
glNewList(displayList,GL_COMPILE_AND_EXECUTE);
glRotatef(ang,iniRotX,iniRotY,iniRotZ);
for(int j=0;j<mdl->groupsSize;j++)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
// Enable Pointers
BuildTexture(j);
//glColor3f(((float)(rand()%256)/256.0f),((float)(rand()%256)/256.0f),((float)(rand()%256)/256.0f));
for(int i=0;i<mdl->groups[j]->capacity.indicesSize;i+=3)
{
int iv1=mdl->groups[j]->indices.vertIndices[i];
int in1=mdl->groups[j]->indices.normalIndices[i];
int it1=mdl->groups[j]->indices.texIndices[i];
int iv2=mdl->groups[j]->indices.vertIndices[i+1];
int in2=mdl->groups[j]->indices.normalIndices[i+1];
int it2=mdl->groups[j]->indices.texIndices[i+1];
int iv3=mdl->groups[j]->indices.vertIndices[i+2];
int in3=mdl->groups[j]->indices.normalIndices[i+2];
int it3=mdl->groups[j]->indices.texIndices[i+2];
glBegin(GL_TRIANGLES);
glTexCoord2f(mdl->groups[j]->texCoords[it1].u,mdl->groups[j]->texCoords[it1].v);
glNormal3f(mdl->groups[j]->normals[in1].x,mdl->groups[j]->normals[in1].y,mdl->groups[j]->normals[in1].z);
glVertex3f(mdl->groups[j]->verts[iv1].x,mdl->groups[j]->verts[iv1].y,mdl->groups[j]->verts[iv1].z);
glTexCoord2f(mdl->groups[j]->texCoords[it2].u,mdl->groups[j]->texCoords[it2].v);
glNormal3f(mdl->groups[j]->normals[in2].x,mdl->groups[j]->normals[in2].y,mdl->groups[j]->normals[in2].z);
glVertex3f(mdl->groups[j]->verts[iv2].x,mdl->groups[j]->verts[iv2].y,mdl->groups[j]->verts[iv2].z);
glTexCoord2f(mdl->groups[j]->texCoords[it3].u,mdl->groups[j]->texCoords[it3].v);
glNormal3f(mdl->groups[j]->normals[in3].x,mdl->groups[j]->normals[in3].y,mdl->groups[j]->normals[in3].z);
glVertex3f(mdl->groups[j]->verts[iv3].x,mdl->groups[j]->verts[iv3].y,mdl->groups[j]->verts[iv3].z);
glEnd();
}
glDisable(GL_TEXTURE_2D);
}
glRotatef(-ang,iniRotX,iniRotY,iniRotZ);
glEndList();
glCallList(displayList);
}
else
glCallList(displayList);
}
}
}
There are no texture indices in OpenGL. This separation of positions and textures is quite common in modelling software, as it is simpler to manage. But in OpenGL, you need to have the exactly same count of texture coordinates as there are positions, so they could both be indexed using the same index.
This will require you to rebuild all the buffers, so that every combination of vertex index, normal index and texcoord index that occurs in your model can be mapped to a single index for OpenGL rendering. I'll try and write some pseudocode:
struct Trinity {
int iv, in, it;
bool operator <(const Trinity &t) const // implement less-than operator as required by std::map
{
return iv < t.iv || (iv == t.iv && (in < t.in || (in == t.in && it < t.it)));
}
};
#include <map>
#include <vector>
for(int j=0;j<mdl->groupsSize;j++) { // for each object ...
int next_index = 0; // next index to be assigned
std::map<int, Trinity> index_map; // map of collada indices to OpenGL indices
std::vector<int> new_indices; // new indices that can be used for drawing with OpenGL
std::vector<float> new_positions, new_texcoords, new_normals; // other arrays
for(int i=0;i<mdl->groups[j]->capacity.indicesSize;i++) { // for each vertex ...
Trinity t;
t.iv = mdl->groups[j]->indices.vertIndices[i];
t.in = mdl->groups[j]->indices.normalIndices[i];
t.it = mdl->groups[j]->indices.texIndices[i];
// go through all the vertices' indices
int index;
if(index_map.find(t) != index_map.end())
index = index_map[t]; // we already saw this combination of indices, reuse it
else {
index = next_index; // this is new; assign a new index
++ next_index;
index_map[t] = index; // store the index in the map
new_positions.push_back(mdl->groups[j]->verts[t.iv].x);
new_positions.push_back(mdl->groups[j]->verts[t.iv].y);
new_positions.push_back(mdl->groups[j]->verts[t.iv].z);
new_texcoords.push_back(mdl->groups[j]->texCoords[t.it].u);
new_texcoords.push_back(mdl->groups[j]->texCoords[t.it].v);
new_normals.push_back(mdl->groups[j]->normals[t.in].x);
new_normals.push_back(mdl->groups[j]->normals[t.in].y);
new_normals.push_back(mdl->groups[j]->normals[t.in].z);
// add this particular combination of coordinates at the end of the buffers
}
// translate the combination of iv, in, it to a single index
new_indices.push_back(index);
// store it in the array
}
// object data ready to pass to VBOs
}
Now you can put new_indices, new_positions, new_normals and new_texcoords to VBOs and render them. I hope this helps ...
精彩评论