diff options
author | Kyle K <kylek389@gmail.com> | 2010-10-03 18:30:04 -0500 |
---|---|---|
committer | Kamil Kaminski <kamilkss@gmail.com> | 2010-10-03 18:30:04 -0500 |
commit | 163b963b1d6c1d0d288e27aeb978ff521a241917 (patch) | |
tree | d4b03049e501f8f068e4cba3a910c1fb04232ad5 /lib/gltools.cpp | |
download | GLPyramid++-163b963b1d6c1d0d288e27aeb978ff521a241917.tar.gz GLPyramid++-163b963b1d6c1d0d288e27aeb978ff521a241917.tar.bz2 GLPyramid++-163b963b1d6c1d0d288e27aeb978ff521a241917.zip |
Initial commit
Diffstat (limited to 'lib/gltools.cpp')
-rw-r--r-- | lib/gltools.cpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/lib/gltools.cpp b/lib/gltools.cpp new file mode 100644 index 0000000..bcdd957 --- /dev/null +++ b/lib/gltools.cpp @@ -0,0 +1,651 @@ +/* + * gltools.c + * Block + * + * Created by Richard Wright on 10/16/06. + * OpenGL SuperBible, 4th Edition + * + */ + +#include "gltools.h" +#include "math3d.h" +#include <assert.h> + +/////////////////////////////////////////////////////////////////////////////// +// Get the OpenGL version number +bool gltGetOpenGLVersion(int &nMajor, int &nMinor) +{ + const char *szVersionString = (const char *)glGetString(GL_VERSION); + if (szVersionString == NULL) + { + nMajor = 0; + nMinor = 0; + return false; + } + + // Get major version number. This stops at the first non numeric character + nMajor = atoi(szVersionString); + + // Get minor version number. Start past the first ".", atoi terminates on first non numeric char. + nMinor = atoi(strstr(szVersionString, ".")+1); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// This function determines if the named OpenGL Extension is supported +// Returns 1 or 0 +int gltIsExtSupported(const char *extension) +{ + GLubyte *extensions = NULL; + const GLubyte *start; + GLubyte *where, *terminator; + + where = (GLubyte *) strchr(extension, ' '); + if (where || *extension == '\0') + return 0; + + extensions = (GLubyte *)glGetString(GL_EXTENSIONS); + + start = extensions; + for (;;) + { + where = (GLubyte *) strstr((const char *) start, extension); + + if (!where) + break; + + terminator = where + strlen(extension); + + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + return 1; + } + start = terminator; + } + return 0; +} + +#ifdef _WIN32 +/////////////////////////////////////////////////////////////////////////////// +// Win32 Only, check for WGL extension +#include "wglext.h" +int gltIsWGLExtSupported(HDC hDC, const char *szExtension) +{ + GLubyte *extensions = NULL; + const GLubyte *start; + GLubyte *where, *terminator; + PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; + + // Just look or the entry point + wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); + if (wglGetExtensionsStringARB == NULL) + return 0; + + where = (GLubyte *) strchr(szExtension, ' '); + if (where || *szExtension == '\0') + return 0; + + extensions = (GLubyte *)wglGetExtensionsStringARB(hDC); + + start = extensions; + for (;;) + { + where = (GLubyte *) strstr((const char *) start, szExtension); + + if (!where) + break; + + terminator = where + strlen(szExtension); + + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + return 1; + } + start = terminator; + } + return 0; +} +#endif + +///////////////////////////////////////////////////////////// +// Get a pointer to an OpenGL extension +// Note on the Mac, this does a lot of work that could be saved +// if you call this function repeatedly. Write your own function that +// gets the bundle once, gets all the function pointers, then releases +// the bundle. +void *gltGetExtensionPointer(const char *szExtensionName) +{ +#ifdef WIN32 + // Well, this one is simple. An OpenGL context must be + // current first. Returns NULL if extension not supported + return (void *)wglGetProcAddress(szExtensionName); +#endif + +#ifdef linux + // Pretty much ditto above + return (void *)glXGetProcAddress((GLubyte *)szExtensionName); +#endif + +#ifdef __APPLE__ + // Mac is a bit more tricky. + // First we need the bundle + CFBundleRef openGL = 0; + SInt16 fwVersion = 0; + SInt32 fwDir = 0; + + if (FindFolder(kSystemDomain, kFrameworksFolderType, kDontCreateFolder, &fwVersion, &fwDir) != noErr) + return NULL; + + FSSpec fSpec; + FSRef fRef; + if (FSMakeFSSpec(fwVersion, fwDir, "\pOpenGL.framework", &fSpec) != noErr) + return NULL; + + FSpMakeFSRef(&fSpec, &fRef); + CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &fRef); + if (!url) + return NULL; + + openGL = CFBundleCreate(kCFAllocatorDefault, url); + CFRelease(url); + + // Then load the function pointer from the bundle + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, szExtensionName, kCFStringEncodingMacRoman); + void *pFunc = CFBundleGetFunctionPointerForName(openGL, string); + + // Release the bundle and string + CFRelease(string); + CFRelease(openGL); + + // Return the function ponter + return pFunc; +#endif +} + +/////////////////////////////////////////////////////////////////// +// Draw the unit axis. A small white sphere represents the origin +// and the three axes are colored Red, Green, and Blue, which +// corresponds to positive X, Y, and Z respectively. Each axis has +// an arrow on the end, and normals are provided should the axes +// be lit. These are all built using the quadric shapes. For best +// results, put this in a display list. +void gltDrawUnitAxes(void) +{ + GLUquadricObj *pObj; // Temporary, used for quadrics + + // Measurements + float fAxisRadius = 0.025f; + float fAxisHeight = 1.0f; + float fArrowRadius = 0.06f; + float fArrowHeight = 0.1f; + + // Setup the quadric object + pObj = gluNewQuadric(); + gluQuadricDrawStyle(pObj, GLU_FILL); + gluQuadricNormals(pObj, GLU_SMOOTH); + gluQuadricOrientation(pObj, GLU_OUTSIDE); + gluQuadricTexture(pObj, GLU_FALSE); + + /////////////////////////////////////////////////////// + // Draw the blue Z axis first, with arrowed head + glColor3f(0.0f, 0.0f, 1.0f); + gluCylinder(pObj, fAxisRadius, fAxisRadius, fAxisHeight, 10, 1); + glPushMatrix(); + glTranslatef(0.0f, 0.0f, 1.0f); + gluCylinder(pObj, fArrowRadius, 0.0f, fArrowHeight, 10, 1); + glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + gluDisk(pObj, fAxisRadius, fArrowRadius, 10, 1); + glPopMatrix(); + + /////////////////////////////////////////////////////// + // Draw the Red X axis 2nd, with arrowed head + glColor3f(1.0f, 0.0f, 0.0f); + glPushMatrix(); + glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + gluCylinder(pObj, fAxisRadius, fAxisRadius, fAxisHeight, 10, 1); + glPushMatrix(); + glTranslatef(0.0f, 0.0f, 1.0f); + gluCylinder(pObj, fArrowRadius, 0.0f, fArrowHeight, 10, 1); + glRotatef(180.0f, 0.0f, 1.0f, 0.0f); + gluDisk(pObj, fAxisRadius, fArrowRadius, 10, 1); + glPopMatrix(); + glPopMatrix(); + + /////////////////////////////////////////////////////// + // Draw the Green Y axis 3rd, with arrowed head + glColor3f(0.0f, 1.0f, 0.0f); + glPushMatrix(); + glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + gluCylinder(pObj, fAxisRadius, fAxisRadius, fAxisHeight, 10, 1); + glPushMatrix(); + glTranslatef(0.0f, 0.0f, 1.0f); + gluCylinder(pObj, fArrowRadius, 0.0f, fArrowHeight, 10, 1); + glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + gluDisk(pObj, fAxisRadius, fArrowRadius, 10, 1); + glPopMatrix(); + glPopMatrix(); + + //////////////////////////////////////////////////////// + // White Sphere at origin + glColor3f(1.0f, 1.0f, 1.0f); + gluSphere(pObj, 0.05f, 15, 15); + + // Delete the quadric + gluDeleteQuadric(pObj); +} + +// For best results, put this in a display list +// Draw a torus (doughnut) at z = fZVal... torus is in xy plane +void gltDrawTorus(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor) +{ + M3DVector3f vNormal; + double majorStep = 2.0f*M3D_PI / numMajor; + double minorStep = 2.0f*M3D_PI / numMinor; + int i, j; + + for (i=0; i<numMajor; ++i) + { + double a0 = i * majorStep; + double a1 = a0 + majorStep; + GLfloat x0 = (GLfloat) cos(a0); + GLfloat y0 = (GLfloat) sin(a0); + GLfloat x1 = (GLfloat) cos(a1); + GLfloat y1 = (GLfloat) sin(a1); + + glBegin(GL_TRIANGLE_STRIP); + for (j=0; j<=numMinor; ++j) + { + double b = j * minorStep; + GLfloat c = (GLfloat) cos(b); + GLfloat r = minorRadius * c + majorRadius; + GLfloat z = minorRadius * (GLfloat) sin(b); + + // First point + glTexCoord2f((float)(i)/(float)(numMajor), (float)(j)/(float)(numMinor)); + vNormal[0] = x0*c; + vNormal[1] = y0*c; + vNormal[2] = z/minorRadius; + m3dNormalizeVector(vNormal); + glNormal3fv(vNormal); + glVertex3f(x0*r, y0*r, z); + + glTexCoord2f((float)(i+1)/(float)(numMajor), (float)(j)/(float)(numMinor)); + vNormal[0] = x1*c; + vNormal[1] = y1*c; + vNormal[2] = z/minorRadius; + m3dNormalizeVector(vNormal); + glNormal3fv(vNormal); + glVertex3f(x1*r, y1*r, z); + } + glEnd(); + } +} + +// For best results, put this in a display list +// Draw a sphere at the origin +void gltDrawSphere(GLfloat fRadius, GLint iSlices, GLint iStacks) +{ + GLfloat drho = (GLfloat)(3.141592653589) / (GLfloat) iStacks; + GLfloat dtheta = 2.0f * (GLfloat)(3.141592653589) / (GLfloat) iSlices; + GLfloat ds = 1.0f / (GLfloat) iSlices; + GLfloat dt = 1.0f / (GLfloat) iStacks; + GLfloat t = 1.0f; + GLfloat s = 0.0f; + GLint i, j; // Looping variables + + for (i = 0; i < iStacks; i++) + { + GLfloat rho = (GLfloat)i * drho; + GLfloat srho = (GLfloat)(sin(rho)); + GLfloat crho = (GLfloat)(cos(rho)); + GLfloat srhodrho = (GLfloat)(sin(rho + drho)); + GLfloat crhodrho = (GLfloat)(cos(rho + drho)); + + // Many sources of OpenGL sphere drawing code uses a triangle fan + // for the caps of the sphere. This however introduces texturing + // artifacts at the poles on some OpenGL implementations + glBegin(GL_TRIANGLE_STRIP); + s = 0.0f; + for ( j = 0; j <= iSlices; j++) + { + GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta; + GLfloat stheta = (GLfloat)(-sin(theta)); + GLfloat ctheta = (GLfloat)(cos(theta)); + + GLfloat x = stheta * srho; + GLfloat y = ctheta * srho; + GLfloat z = crho; + + glTexCoord2f(s, t); + glNormal3f(x, y, z); + glVertex3f(x * fRadius, y * fRadius, z * fRadius); + + x = stheta * srhodrho; + y = ctheta * srhodrho; + z = crhodrho; + glTexCoord2f(s, t - dt); + s += ds; + glNormal3f(x, y, z); + glVertex3f(x * fRadius, y * fRadius, z * fRadius); + } + glEnd(); + + t -= dt; + } +} + +// 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) + +//////////////////////////////////////////////////////////////////// +// Capture the current viewport and save it as a targa file. +// Be sure and call SwapBuffers for double buffered contexts or +// glFinish for single buffered contexts before calling this function. +// Returns 0 if an error occurs, or 1 on success. +GLint gltWriteTGA(const char *szFileName) +{ + FILE *pFile; // File pointer + TGAHEADER tgaHeader; // TGA file header + unsigned long lImageSize; // Size in bytes of image + GLbyte *pBits = NULL; // Pointer to bits + GLint iViewport[4]; // Viewport in pixels + GLenum lastBuffer; // Storage for the current read buffer setting + + // Get the viewport dimensions + glGetIntegerv(GL_VIEWPORT, iViewport); + + // How big is the image going to be (targas are tightly packed) + lImageSize = iViewport[2] * 3 * iViewport[3]; + + // Allocate block. If this doesn't work, go home + pBits = (GLbyte *)malloc(lImageSize); + if (pBits == NULL) + return 0; + + // Read bits from color buffer + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + + // Get the current read buffer setting and save it. Switch to + // the front buffer and do the read operation. Finally, restore + // the read buffer state + glGetIntegerv(GL_READ_BUFFER, (GLint *)&lastBuffer); + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, iViewport[2], iViewport[3], GL_BGR_EXT, GL_UNSIGNED_BYTE, pBits); + glReadBuffer(lastBuffer); + + // Initialize the Targa header + tgaHeader.identsize = 0; + tgaHeader.colorMapType = 0; + tgaHeader.imageType = 2; + tgaHeader.colorMapStart = 0; + tgaHeader.colorMapLength = 0; + tgaHeader.colorMapBits = 0; + tgaHeader.xstart = 0; + tgaHeader.ystart = 0; + tgaHeader.width = iViewport[2]; + tgaHeader.height = iViewport[3]; + tgaHeader.bits = 24; + tgaHeader.descriptor = 0; + + // 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 + + // Attempt to open the file + pFile = fopen(szFileName, "wb"); + if (pFile == NULL) + { + free(pBits); // Free buffer and return error + return 0; + } + + // Write the header + fwrite(&tgaHeader, sizeof(TGAHEADER), 1, pFile); + + // Write the image data + fwrite(pBits, lImageSize, 1, pFile); + + // Free temporary buffer and close the file + free(pBits); + fclose(pFile); + + // Success! + return 1; +} + +//////////////////////////////////////////////////////////////////// +// Allocate memory and load targa bits. Returns pointer to new buffer, +// height, and width of texture, and the OpenGL format of data. +// Call free() on buffer when finished! +// This only works on pretty vanilla targas... 8, 24, or 32 bit color +// only, no palettes, no RLE encoding. +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; +} + +// Rather than malloc/free a block everytime a shader must be loaded, +// I will dedicate a single 4k page for reading in shaders. Thanks to +// modern OS design, this page will be swapped out to disk later if never +// used again after program initialization. Where-as mallocing different size +// shader blocks could lead to heap fragmentation, which would actually be worse. +//#define MAX_SHADER_LENGTH 4096 -> This is defined in gltools.h +static GLubyte shaderText[MAX_SHADER_LENGTH]; + +//////////////////////////////////////////////////////////////// +// Load the shader from the specified file. Returns false if the +// shader could not be loaded +bool bLoadShaderFile(const char *szFile, GLhandleARB shader) +{ + GLint shaderLength = 0; + FILE *fp; + GLcharARB *fsStringPtr[1]; + + // Open the shader file + fp = fopen(szFile, "r"); + if (fp != NULL) + { + // See how long the file s + while (fgetc(fp) != EOF) + shaderLength++; + + // Allocate a block of memory to send in the shader + assert(shaderLength < MAX_SHADER_LENGTH); // make me bigger! + if (shaderLength > MAX_SHADER_LENGTH) + { + fclose(fp); + return false; + } + + // Go back to beginning of file + rewind(fp); + + // Read the whole file in + if (shaderText != NULL) + fread(shaderText, 1, shaderLength, fp); + + // Make sure it is null terminated and close the file + shaderText[shaderLength] = '\0'; + fclose(fp); + } + else + return false; + + // Load the string + fsStringPtr[0] = (GLcharARB *)shaderText; + glShaderSourceARB(shader, 1, (const GLcharARB **)fsStringPtr, NULL); + + return true; +} + +///////////////////////////////////////////////////////////////// +// Load a pair of shaders, compile, and link together. Specify the complete +// path and file name of each ASCII shader file. Note, there is no support for +// just loading say a vertex program... you have to do both. +GLhandleARB gltLoadShaderPair(const char *szVertexProg, const char *szFragmentProg) +{ + // Temporary Shader objects + GLhandleARB hVertexShader; + GLhandleARB hFragmentShader; + GLhandleARB hReturn = 0; + GLint testVal; + + // Create shader objects + hVertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + hFragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + // Load them. If fail clean up and return null + if (bLoadShaderFile(szVertexProg, hVertexShader) == false) + { + glDeleteObjectARB(hVertexShader); + glDeleteObjectARB(hFragmentShader); + return 0; + } + + if (bLoadShaderFile(szFragmentProg, hFragmentShader) == false) + { + glDeleteObjectARB(hVertexShader); + glDeleteObjectARB(hFragmentShader); + return 0; + } + + // Compile them + glCompileShaderARB(hVertexShader); + glCompileShaderARB(hFragmentShader); + + // Check for errors + glGetObjectParameterivARB(hVertexShader, GL_OBJECT_COMPILE_STATUS_ARB, &testVal); + if (testVal == GL_FALSE) + { + glDeleteObjectARB(hVertexShader); + glDeleteObjectARB(hFragmentShader); + return 0; + } + + glGetObjectParameterivARB(hFragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &testVal); + if (testVal == GL_FALSE) + { + glDeleteObjectARB(hVertexShader); + glDeleteObjectARB(hFragmentShader); + return 0; + } + + // Link them - assuming it works... + hReturn = glCreateProgramObjectARB(); + glAttachObjectARB(hReturn, hVertexShader); + glAttachObjectARB(hReturn, hFragmentShader); + glLinkProgramARB(hReturn); + + // These are no longer needed + glDeleteObjectARB(hVertexShader); + glDeleteObjectARB(hFragmentShader); + + return hReturn; +} + |