// http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=45 #include #include "SDL.h" #include "sdl-timer.hpp" #define SCREEN_WIDTH 1024 #define SCREEN_HEIGHT 768 #define SCREEN_BPP 32 #define FRAMES_PER_SECOND 60 #define MESH_RESOLUTION 4.0f #define MESH_HEIGHTSCALE 1.0f //#define GL_ARRAY_BUFFER_ARB 0x8892 //#define GL_STATIC_DRAW_ARB 0x88E4 class CVert { public: float x; float y; float z; }; typedef CVert CVec; class CTexCoord { public: float u; float v; }; class CMesh { public: int m_nVertexCount; CVert * m_pVertices; CTexCoord* m_pTexCoords; GLuint m_nTextureId; GLuint m_nVBOVertices; GLuint m_nVBOTexCoords; SDL_Surface* m_pTextureImage; public: CMesh(); ~CMesh(); bool LoadHeightmap(char* czPath, float flHeightScale, float flResolution); float PtHeight(int nX, int nY); void BuildVBOs(); }; bool g_fVGOSupported = true; CMesh* g_pMesh = NULL; bool g_fVBOSupported = false; float g_flYRot = 0.0f; bool CMesh :: LoadHeightmap(char* szPath, float flHeightScale, float flResolution) { SDL_Surface *surface; Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN //Check Endianess and set rgba masks accordingly rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x00000000; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0x00000000; #endif m_pTextureImage = SDL_LoadBMP(szPath); surface = SDL_CreateRGBSurface(SDL_SWSURFACE, m_pTextureImage->w, m_pTextureImage->h, 24, rmask, gmask, bmask, amask); m_pTextureImage = SDL_ConvertSurface(m_pTextureImage, surface->format, SDL_SWSURFACE); // generate vertex field m_nVertexCount = (int) (m_pTextureImage->w * m_pTextureImage->h * 6/(flResolution * flResolution)); printf("vertex count=%d\n",m_nVertexCount); printf("image width=%d, height=%d\n", m_pTextureImage->w, m_pTextureImage->h); m_pVertices = new CVec[m_nVertexCount]; m_pTexCoords = new CTexCoord[m_nVertexCount]; int nX, nZ, nTri, nIndex = 0; float flX, flZ; for (nZ = 0; nZ < m_pTextureImage->h; nZ +=(int) flResolution) { for (nX = 0; nX < m_pTextureImage->w; nX +=(int) flResolution) { for (nTri = 0; nTri < 6; nTri++) { flX=(float) nX + ((nTri == 1 || nTri == 2 || nTri == 5) ? flResolution : 0.0f); flZ=(float) nZ + ((nTri == 2 || nTri == 4 || nTri == 5) ? flResolution : 0.0f); m_pVertices[nIndex].x = flX - (m_pTextureImage->w/2); m_pVertices[nIndex].y = PtHeight((int) flX, (int) flZ)*flHeightScale; m_pVertices[nIndex].z = flZ - (m_pTextureImage->h/2); m_pTexCoords[nIndex].u = flX/m_pTextureImage->w; m_pTexCoords[nIndex].v = flZ/m_pTextureImage->h; #if 0 printf("%d (%f,%f,%f) [%f %f]\n", nIndex, m_pVertices[nIndex].x, m_pVertices[nIndex].y, m_pVertices[nIndex].z, m_pTexCoords[nIndex].u, m_pTexCoords[nIndex].v); #endif nIndex++; } } } glGenTextures(1, &m_nTextureId); glBindTexture(GL_TEXTURE_2D, m_nTextureId); glTexImage2D(GL_TEXTURE_2D, 0, 3, m_pTextureImage->w, m_pTextureImage->h, 0, GL_RGB, GL_UNSIGNED_BYTE, m_pTextureImage->pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if(m_pTextureImage) { SDL_FreeSurface(m_pTextureImage); } if(surface) { SDL_FreeSurface(surface); } return true; } CMesh :: CMesh() { // Set Pointers To NULL m_pTextureImage = NULL; m_pVertices = NULL; m_pTexCoords = NULL; m_nVertexCount = 0; m_nVBOVertices = m_nVBOTexCoords = m_nTextureId = 0; } CMesh :: ~CMesh() { // Delete VBOs if( g_fVBOSupported ) { GLuint nBuffers[2] = { m_nVBOVertices, m_nVBOTexCoords }; glDeleteBuffers( 2, nBuffers ); // Free The Memory } // Delete Data if( m_pVertices ) // Deallocate Vertex Data delete [] m_pVertices; m_pVertices = NULL; if( m_pTexCoords ) // Deallocate Texture Coord Data delete [] m_pTexCoords; m_pTexCoords = NULL; if(m_nVBOTexCoords) // Deallocate Vertex Buffer glDeleteBuffers( 1, &m_nVBOTexCoords); if(m_nVBOVertices) glDeleteBuffers( 1, &m_nVBOVertices); } float CMesh :: PtHeight (int nX, int nY) { SDL_Color color; Uint32 col=0; char* offset = (char*) m_pTextureImage->pixels; offset += (m_pTextureImage->pitch * nY); offset += (m_pTextureImage->format->BytesPerPixel * nX); memcpy(&col, offset, m_pTextureImage->format->BytesPerPixel); SDL_GetRGB(col, m_pTextureImage->format, &color.r, &color.g, &color.b); return (0.299f * color.r + 0.587f * color.g + 0.114f * color.b); } void CMesh::BuildVBOs() { printf("building VBOs\n"); glGenBuffers(1, &m_nVBOVertices); glBindBuffer(GL_ARRAY_BUFFER, m_nVBOVertices); glBufferData(GL_ARRAY_BUFFER, m_nVertexCount * 3 * sizeof(float), m_pVertices, GL_STATIC_DRAW); glGenBuffers(1, &m_nVBOTexCoords); glBindBuffer(GL_ARRAY_BUFFER, m_nVBOTexCoords); glBufferData(GL_ARRAY_BUFFER, m_nVertexCount * 2 * sizeof(float), m_pTexCoords, GL_STATIC_DRAW); delete [] m_pVertices; m_pVertices = NULL; delete [] m_pTexCoords; m_pTexCoords = NULL; } // straight from tutorial #if 0 bool IsExtensionSupported( char* szTargetExtension ) { const unsigned char *pszExtensions = NULL; const unsigned char *pszStart; unsigned char *pszWhere, *pszTerminator; // Extension names should not have spaces pszWhere = (unsigned char *) strchr( szTargetExtension, ' ' ); if( pszWhere || *szTargetExtension == '\0' ) return false; // Get Extensions String pszExtensions = glGetString( GL_EXTENSIONS ); // Search The Extensions String For An Exact Copy pszStart = pszExtensions; for(;;) { pszWhere = (unsigned char *) strstr( (const char *) pszStart, szTargetExtension ); if( !pszWhere ) break; pszTerminator = pszWhere + strlen( szTargetExtension ); if( pszWhere == pszStart || *( pszWhere - 1 ) == ' ' ) if( *pszTerminator == ' ' || *pszTerminator == '\0' ) return true; pszStart = pszTerminator; } return false; } #endif bool Initialize (void) { g_pMesh = new CMesh(); printf("loading data/terrain.bmp\n"); if(!g_pMesh->LoadHeightmap("data/terrain.bmp", MESH_HEIGHTSCALE, MESH_RESOLUTION)) { fprintf(stderr, "failed to load data/terrain.bmp\n"); return false; } #if 0 printf("checking for VBO support..."); g_fVBOSupported = glewIsExtensionSupported("GL_vertex_buffer_object"); if(g_fVBOSupported) { printf("supported!\n"); g_pMesh->BuildVBOs(); }else{ printf("NOT supported :(\n"); } #endif printf("about to build VBOs\n"); g_pMesh->BuildVBOs(); printf("done building VBOs\n"); return true; } bool init_GL(); SDL_Event event; GLuint texture[1]; class Square { private: int x, y, width, height; int xVel, yVel; public: Square(); void handle_input(); void move(); }; Square::Square() { x = 5; y = 5; width = 256; height = 256; xVel = 0; yVel = 0; } void Square::move() { x += xVel; if ((x < 0) || (x + width > SCREEN_WIDTH)) { x -= xVel; } y += yVel; if ((y < 0) || (y + width > SCREEN_HEIGHT)) { y -= yVel; } } void Square::handle_input() { if (event.type == SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_UP: yVel -= height / 2; break; case SDLK_DOWN: yVel += height / 2; break; case SDLK_LEFT: xVel -= width / 2; break; case SDLK_RIGHT: xVel += width / 2; break; default: break; } } else { switch (event.key.keysym.sym) { case SDLK_UP: yVel += height / 2; break; case SDLK_DOWN: yVel -= height / 2; break; case SDLK_LEFT: xVel += width / 2; break; case SDLK_RIGHT: xVel -= width / 2; break; default: break; } } } bool init() { if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { return false; } if (SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL) == NULL) { return false; } if (init_GL() == false) { return false; } if (Initialize() == false) { return false; } SDL_WM_SetCaption("OpenGL Test", NULL); return true; } bool init_GL() { glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glClearDepth(1.0f); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glClearColor(0.0f, 0.5f, 1.0f, 0.5f); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); if (glGetError() != GL_NO_ERROR) { return false; } return true; } void clean_up() { SDL_Quit(); } void draw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, -220.0f, 0.0f); glRotatef(10.0f, 1.0f, 0.0f, 0.0f); glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); if(g_fVBOSupported) { glBindBuffer(GL_ARRAY_BUFFER, g_pMesh->m_nVBOVertices); glVertexPointer(3, GL_FLOAT, 0, (char *) NULL); glBindBuffer(GL_ARRAY_BUFFER, g_pMesh->m_nVBOTexCoords); glTexCoordPointer(2, GL_FLOAT, 0, (char *) NULL); }else { glVertexPointer(3, GL_FLOAT, 0, g_pMesh->m_pVertices); glTexCoordPointer(2, GL_FLOAT, 0, g_pMesh->m_pTexCoords); } glDrawArrays(GL_TRIANGLES, 0, g_pMesh->m_nVertexCount); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); SDL_GL_SwapBuffers(); } int main(int argc, char* args[]) { Square square; Timer fps; // make sure the program waits for a quit bool quit = false; if (init() == false) { fprintf(stderr, "init failed\n"); return 1; } while (quit == false) { fps.start(); while (SDL_PollEvent(&event)) { // square.handle_input(); if (event.type == SDL_QUIT) { quit = true; } } draw(); if (fps.get_ticks() < 1000 / FRAMES_PER_SECOND) { SDL_Delay((1000/FRAMES_PER_SECOND) - fps.get_ticks()); } } clean_up(); return 0; }