/* * 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; }