-
OpenGL VBO's
Ok, I'm reading and "following along" with the OpenGL SuperBible (4th), and I've hit VBO's. After a good 3 hours of going over it with a fine toothed (and very frustrated) comb, what I've got just doesn't work.
I'm trying to render a single textured quad. Note that if I stop using a VBO and uncomment the immediate mode stuff, it all works fine.
This is kinda big, please ignore the messiness, it's just a fiddle after all. Please refrain from ..........ing / commenting on code other than the VBO-related stuff, I'm seriously not in the mood to care (I might feel different in the morning, though, so go nuts if you really can't help yourself).
Some of this code (gltLoadTGA, the M3D<whatever>) is from the book, just noting.
Code:
#include "Common.h"
#include "math3d.h"
#include <vector>
#include <string>
const double PI = 3.14159265358979323846;
const double PI_2 = PI * 2;
const double PI_OVER_180 = PI / 180.0;
//GLfloat vOrigin[] = {0.0f, 0.0f, 0.0f};
//GLfloat vAmbientLight[] = {0.3f, 0.3f, 0.3f, 1.0f};
//GLfloat vDiffuseLight[] = {0.7f, 0.7f, 0.7f, 1.0f};
//GLfloat vSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f};
//GLfloat vGrey[] = {0.75f, 0.75f, 0.75f, 1.0f};
//
//GLfloat xRot = 0.0f;
//GLfloat yRot = 0.0f;
//GLfloat zRot = 0.0f;
//GLdouble dFov = 60.0;
//GLfloat zTrans = -100.0f;
//int specShininess = 128;
//bool smoothShading = true;
//bool depthTesting = true;
//bool cullFace = true;
//bool wireframe = false;
//bool light0 = true;
GLfloat red = 1.0f;
GLfloat green = 1.0f;
GLfloat blue = 1.0f;
GLint filtering = GL_LINEAR;
const int TEXTURE_COUNT = 1;
GLuint textures[TEXTURE_COUNT];
std::string resident("UNKNOWN");
GLboolean residences[TEXTURE_COUNT];
template <typename T> inline void SafeDelete(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = NULL;
}
}
template <typename T> inline void SafeDeleteArray(T*& ptr)
{
if (ptr)
{
delete[] ptr;
ptr = NULL;
}
}
class VBO
{
GLfloat* m_vVerts;
GLfloat* m_vTexCoords;
GLushort* m_Indices;
GLuint m_BufferObjects[3];
int m_NumVerts;
public:
VBO()
: m_vVerts(NULL),
m_vTexCoords(NULL),
m_Indices(NULL),
m_NumVerts(0)
{
}
~VBO()
{
Cleanup();
}
void BeginMesh(GLuint requiredVerts)
{
Cleanup();
std::cout << "Allocating " << requiredVerts * 2 << " bytes for vertices and tex coords\n";
std::cout << "Allocating " << requiredVerts << " bytes for indices\n";
m_vVerts = new GLfloat[requiredVerts * 2];
m_vTexCoords = new GLfloat[requiredVerts * 2];
m_Indices = new GLushort[requiredVerts];
}
void AddVertex(M3DVector2f vert, M3DVector2f vTexCoord)
{
memcpy(m_vVerts + (m_NumVerts * 2), vert, sizeof(M3DVector2f));
memcpy(m_vTexCoords + (m_NumVerts * 2), vTexCoord, sizeof(M3DVector2f));
m_Indices[m_NumVerts] = (GLushort) m_NumVerts;
m_NumVerts++;
}
void EndMesh()
{
if (!m_NumVerts)
{
std::cout << "VBO::EndMesh(): No verts specified\n";
return;
}
glGenBuffers(3, m_BufferObjects);
// Copy data to vid mem
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjects[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_NumVerts * 2, m_vVerts, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjects[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_NumVerts * 2, m_vTexCoords, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferObjects[2]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * m_NumVerts, m_Indices, GL_STATIC_DRAW);
// Can safely free the vert memory now since GL copies
Cleanup();
}
void Draw()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Verts
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjects[0]);
glVertexPointer(2, GL_FLOAT, 0, 0);
// Tex coords
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjects[1]);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferObjects[2]);
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//GLenum err = glGetError();
//if (err != GL_NO_ERROR)
// std::cout << "........\n";
}
void DumpVerts()
{
int i = 0;
std::cout << "\n" << m_NumVerts << " vertices\n";
for ( ; i < m_NumVerts*2; i++)
{
std::cout << m_vVerts[i];
if (!(i % 2))
std::cout << ",";
else
std::cout << "\n";
}
for (i = 0; i < m_NumVerts * 2; i++)
{
std::cout << m_vTexCoords[i];
if (!(i % 2))
std::cout << ",";
else
std::cout << "\n";
}
for (i = 0; i < m_NumVerts; i++)
std::cout << m_Indices[i] << "\n";
}
private:
void Cleanup()
{
SafeDeleteArray(m_vVerts);
SafeDeleteArray(m_vTexCoords);
SafeDeleteArray(m_Indices);
m_NumVerts = 0;
glDeleteBuffers(3, m_BufferObjects);
}
};
VBO vbo;
GLfloat mIdentity[] = { 1.0f, 0.0f, 0.0f, 0.0f, // X column
0.0f, 1.0f, 0.0f, 0.0f, // Y column
0.0f, 0.0f, 1.0f, 0.0f, // Z column
0.0f, 0.0f, 0.0f, 1.0f }; // Translation
// Define targa header. This is only used locally.
#pragma pack(1)
typedef struct
{
GLbyte identsize; // Size of ID field that follows header (0)
GLbyte colorMapType; // 0 = None, 1 = paletted
GLbyte imageType; // 0 = none, 1 = indexed, 2 = rgb, 3 = grey, +8=rle
unsigned short colorMapStart; // First colour map entry
unsigned short colorMapLength; // Number of colors
unsigned char colorMapBits; // bits per palette entry
unsigned short xstart; // image x origin
unsigned short ystart; // image y origin
unsigned short width; // width in pixels
unsigned short height; // height in pixels
GLbyte bits; // bits per pixel (8 16, 24, 32)
GLbyte descriptor; // image descriptor
} TGAHEADER;
#pragma pack(8)
GLbyte *gltLoadTGA(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
FILE *pFile; // File pointer
TGAHEADER tgaHeader; // TGA file header
unsigned long lImageSize; // Size in bytes of image
short sDepth; // Pixel depth;
GLbyte *pBits = NULL; // Pointer to bits
// Default/Failed values
*iWidth = 0;
*iHeight = 0;
*eFormat = GL_BGR_EXT;
*iComponents = GL_RGB8;
// Attempt to open the fil
pFile = fopen(szFileName, "rb");
if(pFile == NULL)
return NULL;
// Read in header (binary)
fread(&tgaHeader, 18/* sizeof(TGAHEADER)*/, 1, pFile);
// Do byte swap for big vs little endian
#ifdef __APPLE__
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
LITTLE_ENDIAN_WORD(&tgaHeader.width);
LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif
// Get width, height, and depth of texture
*iWidth = tgaHeader.width;
*iHeight = tgaHeader.height;
sDepth = tgaHeader.bits / 8;
// Put some validity checks here. Very simply, I only understand
// or care about 8, 24, or 32 bit targa's.
if(tgaHeader.bits != 8 && tgaHeader.bits != 24 && tgaHeader.bits != 32)
return NULL;
// Calculate size of image buffer
lImageSize = tgaHeader.width * tgaHeader.height * sDepth;
// Allocate memory and check for success
pBits = (GLbyte*)malloc(lImageSize * sizeof(GLbyte));
if(pBits == NULL)
return NULL;
// Read in the bits
// Check for read error. This should catch RLE or other
// weird formats that I don't want to recognize
if(fread(pBits, lImageSize, 1, pFile) != 1)
{
free(pBits);
return NULL;
}
// Set OpenGL format expected
switch(sDepth)
{
case 3: // Most likely case
*eFormat = GL_BGR_EXT;
*iComponents = GL_RGB8;
break;
case 4:
*eFormat = GL_BGRA_EXT;
*iComponents = GL_RGBA8;
break;
case 1:
*eFormat = GL_LUMINANCE;
*iComponents = GL_LUMINANCE8;
break;
};
// Done with File
fclose(pFile);
// Return pointer to image data
return pBits;
}
template <typename T> inline T Clamp(T value, T min, T max)
{
if (value < min)
value = min;
if (value > max)
value = max;
return value;
}
template <typename T> inline T ClampInPlace(T& value, T min, T max)
{
if (value < min)
value = min;
if (value > max)
value = max;
return value;
}
void ChangeSize(GLsizei width, GLsizei height);
inline double DegToRad(double degrees)
{
return degrees * PI_OVER_180;
}
inline double RadToDeg(double radians)
{
return 180.0 * (PI / radians);
}
const char* VPrintf(const char* format, ...)
{
static char buffers[3][1024];
static int index = 0;
va_list args;
char* destBuff = buffers[index++ & 1];
va_start(args, format);
vsprintf(destBuff, format, args);
va_end(args);
return destBuff;
}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT);
//glutSetWindowTitle(VPrintf("xRot: %f - yRot: %f - zRot: %f - zTrans: %f - FOV: %f - Spec: %d",
// xRot, yRot, zRot, zTrans, dFov, specShininess));
//if (smoothShading)
// glShadeModel(GL_SMOOTH);
//else
// glShadeModel(GL_FLAT);
//
//if (depthTesting)
// glEnable(GL_DEPTH_TEST);
//else
// glDisable(GL_DEPTH_TEST);
//
//if (cullFace)
// glEnable(GL_CULL_FACE);
//else
// glDisable(GL_CULL_FACE);
//
//if (wireframe)
//{
// glPolygonMode(GL_BACK, GL_LINE);
// glPolygonMode(GL_FRONT, GL_LINE);
//}
//else
//{
// glPolygonMode(GL_BACK, GL_FILL);
// glPolygonMode(GL_FRONT, GL_FILL);
//}
//
//if (light0)
// glEnable(GL_LIGHT0);
//else
// glDisable(GL_LIGHT0);
// Pull back along the Z
//glTranslatef(0.0f, 0.0f, zTrans);
//M3DMatrix44f mTransZ;
//m3dTranslationMatrix44(mTransZ, 0.0f, 0.0f, zTrans);
//glMultMatrixf(mTransZ);
glAreTexturesResident(TEXTURE_COUNT, textures, residences);
if (residences[0])
resident = "RESIDENT";
else
resident = "NOT RESIDENT";
glutSetWindowTitle(VPrintf("R: %f - G: %f - B: %f - %s", red, green, blue, resident.c_str()));
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
glColor3f(ClampInPlace(red, 0.0f, 1.0f), ClampInPlace(green, 0.0f, 1.0f), ClampInPlace(blue, 0.0f, 1.0f));
vbo.Draw();
//glBegin(GL_QUADS);
// glTexCoord2i(0, 0);
// glVertex2i(100, 200);
//
// glTexCoord2i(1, 0);
// glVertex2i(200, 200);
//
// glTexCoord2i(1, 1);
// glVertex2i(200, 100);
//
// glTexCoord2i(0, 1);
// glVertex2i(100, 100);
//glEnd();
glutSwapBuffers();
}
void SpecialKeys(int key, int x, int y)
{
//switch (key)
//{
//case GLUT_KEY_LEFT:
// yRot -= 5.0f;
// break;
//case GLUT_KEY_RIGHT:
// yRot += 5.0f;
// break;
//case GLUT_KEY_UP:
// xRot -= 5.0f;
// break;
//case GLUT_KEY_DOWN:
// xRot += 5.0f;
// break;
//case GLUT_KEY_PAGE_UP:
// zRot += 5.0f;
// break;
//case GLUT_KEY_PAGE_DOWN:
// zRot -= 5.0f;
// break;
//case GLUT_KEY_HOME:
// zTrans += 10.0f;
// break;
//case GLUT_KEY_END:
// zTrans -= 10.0f;
// break;
//case GLUT_KEY_F1:
// smoothShading = !smoothShading;
// break;
//case GLUT_KEY_F2:
// depthTesting = !depthTesting;
// break;
//case GLUT_KEY_F3:
// cullFace = !cullFace;
// break;
//case GLUT_KEY_F4:
// wireframe = !wireframe;
// break;
//case GLUT_KEY_F5:
// dFov += 10.0;
// ChangeSize(800, 600);
// break;
//case GLUT_KEY_F6:
// dFov -= 10.0;
// ChangeSize(800, 600);
// break;
//case GLUT_KEY_F7:
// light0 = !light0;
// break;
//case GLUT_KEY_F8:
// specShininess += 1;
// break;
//case GLUT_KEY_F9:
// specShininess -= 1;
// break;
//}
switch (key)
{
case GLUT_KEY_F1:
red += 0.1f;
break;
case GLUT_KEY_F2:
red -= 0.1f;
break;
case GLUT_KEY_F3:
green += 0.1f;
break;
case GLUT_KEY_F4:
green -= 0.1f;
break;
case GLUT_KEY_F5:
blue += 0.1f;
break;
case GLUT_KEY_F6:
blue -= 0.1f;
break;
case GLUT_KEY_F11:
filtering = GL_LINEAR;
break;
case GLUT_KEY_F12:
filtering = GL_NEAREST;
break;
}
glutPostRedisplay();
}
void TimerFunction(int value)
{
glutPostRedisplay();
glutTimerFunc(33, TimerFunction, 1);
}
void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLbyte* bytes;
GLint width, height, comp;
GLenum fmt;
glGenTextures(TEXTURE_COUNT, textures);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
bytes = gltLoadTGA("AILogo.tga", &width, &height, &comp, &fmt);
glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height, 0, fmt, GL_UNSIGNED_BYTE, bytes);
free(bytes);
glAreTexturesResident(TEXTURE_COUNT, textures, residences);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//glBegin(GL_QUADS);
// glTexCoord2i(0, 0);
// glVertex2i(100, 200);
//
// glTexCoord2i(1, 0);
// glVertex2i(200, 200);
//
// glTexCoord2i(1, 1);
// glVertex2i(200, 100);
//
// glTexCoord2i(0, 1);
// glVertex2i(100, 100);
//glEnd();
M3DVector2f lowerLeft = {100, 200};
M3DVector2f lowerLeftT = {0, 0};
M3DVector2f lowerRight = {200, 200};
M3DVector2f lowerRightT = {1, 0};
M3DVector2f upperRight = {200, 100};
M3DVector2f upperRightT = {1, 1};
M3DVector2f upperLeft = {100, 100};
M3DVector2f upperLeftT = {0, 1};
vbo.BeginMesh(4);
vbo.AddVertex(lowerLeft, lowerLeftT);
vbo.AddVertex(lowerRight, lowerRightT);
vbo.AddVertex(upperRight, upperRightT);
vbo.AddVertex(upperLeft, upperLeftT);
vbo.DumpVerts();
vbo.EndMesh();
/*
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
If you want to simply replace the color of the underlying geometry, you can specify
GL_REPLACE for the environment mode. Doing so replaces fragment colors from the geom-
etry directly with the texel colors. Making this change eliminates any effect on the texture
from the underlying geometry. If the texture has an alpha channel, you can enable blend-
ing (or use the alpha test), and you can use this mode to create transparent geometry
patterned after the alpha channel in the texture map.
*/
glEnable(GL_TEXTURE_2D);
//glShadeModel(GL_SMOOTH);
//glEnable(GL_DEPTH_TEST);
//glEnable(GL_STENCIL_TEST);
//glEnable(GL_LIGHTING);
//glEnable(GL_COLOR_MATERIAL);
//glLightModelfv(GL_LIGHT_MODEL_AMBIENT, vAmbientLight);
//glLightfv(GL_LIGHT0, GL_AMBIENT, vAmbientLight);
//glLightfv(GL_LIGHT0, GL_DIFFUSE, vDiffuseLight);
//glLightfv(GL_LIGHT0, GL_SPECULAR, vSpecular);
//glDisable(GL_BLEND);
//std::cout << "OpenGL Setup\n\n";
//int stackDepth;
//glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &stackDepth);
//std::cout << "Modelview matrix stack depth: " << stackDepth << "\n";
}
void ShutdownRC()
{
glDeleteTextures(TEXTURE_COUNT, textures);
}
void ChangeSize(GLsizei width, GLsizei height)
{
if (height == 0)
height = 1;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(mIdentity);
//GLfloat aspectRatio = (GLfloat) width / (GLfloat) height;
//gluPerspective(dFov, aspectRatio, 1.0, -1.0);
glOrtho(0, width, height, 0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(mIdentity);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL Fiddle");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
//glutTimerFunc(33, TimerFunction, 1);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}