#include "PolyVoxCore/BlockVolume.h" #include "PolyVoxCore/IndexedSurfacePatch.h" #include "PolyVoxCore/SurfaceExtractors.h" #include "PolyVoxCore/Utility.h" #include // Standard Header For Most Programs #ifdef WIN32 #include "glew/glew.h" #else #include // The GL Header File #endif #include // The GL Utility Toolkit (Glut) Header #include //Some namespaces we need using namespace std; using namespace PolyVox; using namespace std; struct OpenGLSurfacePatch { GLulong noOfIndices; GLuint indexBuffer; GLuint vertexBuffer; }; //Global variables are easier for demonstration purposes, especially //as I'm not sure how/if I can pass variables to the GLUT functions. //Global variables are denoted by the 'g_' prefix const uint16 g_uVolumeSideLength = 128; const uint16 g_uRegionSideLength = 16; const uint16 g_uVolumeSideLengthInRegions = g_uVolumeSideLength / g_uRegionSideLength; //Creates a volume 128x128x128 BlockVolume g_volData(logBase2(g_uVolumeSideLength)); //Rather than storing one big mesh, the volume is broken into regions and a mesh is stored for each region OpenGLSurfacePatch g_openGLSurfacePatches[g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions]; OpenGLSurfacePatch BuildOpenGLSurfacePatch(IndexedSurfacePatch& isp) { OpenGLSurfacePatch result; const vector& vecVertices = isp.getVertices(); const vector& vecIndices = isp.getIndices(); glGenBuffers(1, &result.indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.indexBuffer); int s = vecIndices.size() * sizeof(GLint); if(s != 0) { GLvoid* blah = (GLvoid*)(&(vecIndices[0])); glBufferData(GL_ELEMENT_ARRAY_BUFFER, s, blah, GL_STATIC_DRAW); } result.noOfIndices = vecIndices.size(); glGenBuffers(1, &result.vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, result.vertexBuffer); glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(GLfloat) * 6, 0, GL_STATIC_DRAW); GLfloat* ptr = (GLfloat*)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); for(vector::const_iterator iterVertex = vecVertices.begin(); iterVertex != vecVertices.end(); ++iterVertex) { const SurfaceVertex& vertex = *iterVertex; const Vector3DFloat& v3dVertexPos = vertex.getPosition(); //const Vector3DFloat v3dRegionOffset(uRegionX * g_uRegionSideLength, uRegionY * g_uRegionSideLength, uRegionZ * g_uRegionSideLength); const Vector3DFloat v3dFinalVertexPos = v3dVertexPos + static_cast(isp.m_v3dRegionPosition); *ptr = v3dFinalVertexPos.getX(); ptr++; *ptr = v3dFinalVertexPos.getY(); ptr++; *ptr = v3dFinalVertexPos.getZ(); ptr++; GLfloat red = 1.0f; GLfloat green = 0.0f; GLfloat blue = 0.0f; uint8 material = vertex.getMaterial() + 0.5; switch(material) { case 1: red = 1.0; green = 0.0; blue = 0.0; break; case 2: red = 0.0; green = 1.0; blue = 0.0; break; case 3: red = 0.0; green = 0.0; blue = 1.0; break; case 4: red = 1.0; green = 1.0; blue = 0.0; break; case 5: red = 1.0; green = 0.0; blue = 1.0; break; } *ptr = red; ptr++; *ptr = green; ptr++; *ptr = blue; ptr++; } glUnmapBuffer(GL_ARRAY_BUFFER); return result; } void createSphere(float fRadius, uint8 uValue) { //This vector hold the position of the center of the volume Vector3DFloat v3dVolCenter(g_volData.getSideLength() / 2, g_volData.getSideLength() / 2, g_volData.getSideLength() / 2); //This three-level for loop iterates over every voxel in the volume for (int z = 0; z < g_volData.getSideLength(); z++) { for (int y = 0; y < g_volData.getSideLength(); y++) { for (int x = 0; x < g_volData.getSideLength(); x++) { //Store our current position as a vector... Vector3DFloat v3dCurrentPos(x,y,z); //And compute how far the current position is from the center of the volume float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length(); //If the current voxel is less than 'radius' units from the center //then we make it solid, otherwise we make it empty space. if(fDistToCenter <= fRadius) { g_volData.setVoxelAt(x,y,z, uValue); } } } } } void createCube(Vector3DUint16 lowerCorner, Vector3DUint16 upperCorner, uint8 uValue) { //This three-level for loop iterates over every voxel between the specified corners for (int z = lowerCorner.getZ(); z <= upperCorner.getZ(); z++) { for (int y = lowerCorner.getY(); y <= upperCorner.getY(); y++) { for (int x = lowerCorner.getX() ; x <= upperCorner.getX(); x++) { g_volData.setVoxelAt(x,y,z, uValue); } } } } void init ( GLvoid ) // Create Some Everyday Functions { glShadeModel(GL_SMOOTH); // Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glEnable ( GL_COLOR_MATERIAL ); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } void display ( void ) // Create The Display Function { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer glLoadIdentity(); // Reset The Current Modelview Matrix glTranslatef(-g_uVolumeSideLength/2,-g_uVolumeSideLength/2,-200.0f); for(uint16 uRegionZ = 0; uRegionZ < g_uVolumeSideLengthInRegions; ++uRegionZ) { for(uint16 uRegionY = 0; uRegionY < g_uVolumeSideLengthInRegions; ++uRegionY) { for(uint16 uRegionX = 0; uRegionX < g_uVolumeSideLengthInRegions; ++uRegionX) { glBindBuffer(GL_ARRAY_BUFFER, g_openGLSurfacePatches[uRegionX][uRegionY][uRegionZ].vertexBuffer); glVertexPointer(3, GL_FLOAT, 24, 0); glColorPointer(3, GL_FLOAT, 24, (GLvoid*)12); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_openGLSurfacePatches[uRegionX][uRegionY][uRegionZ].indexBuffer); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glDrawElements(GL_TRIANGLE_STRIP, g_openGLSurfacePatches[uRegionX][uRegionY][uRegionZ].noOfIndices, GL_UNSIGNED_INT, 0); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } } } GLenum errCode; const GLubyte *errString; if ((errCode = glGetError()) != GL_NO_ERROR) { errString = gluErrorString(errCode); cout << "OpenGL Error: " << errString << endl; } glutSwapBuffers ( ); // Swap The Buffers To Not Be Left With A Clear Screen } void reshape ( int w, int h ) // Create The Reshape Function (the viewport) { glViewport ( 0, 0, w, h ); glMatrixMode ( GL_PROJECTION ); // Select The Projection Matrix glLoadIdentity ( ); // Reset The Projection Matrix if ( h==0 ) // Calculate The Aspect Ratio Of The Window gluPerspective ( 80, ( float ) w, 1.0, 5000.0 ); else gluPerspective ( 80, ( float ) w / ( float ) h, 1.0, 5000.0 ); glMatrixMode ( GL_MODELVIEW ); // Select The Model View Matrix glLoadIdentity ( ); // Reset The Model View Matrix } void keyboard ( unsigned char key, int x, int y ) // Create Keyboard Function { switch ( key ) { case 27: // When Escape Is Pressed... exit ( 0 ); // Exit The Program break; // Ready For Next Case default: // Now Wrap It Up break; } } void arrow_keys ( int a_keys, int x, int y ) // Create Special Function (required for arrow keys) { switch ( a_keys ) { case GLUT_KEY_UP: // When Up Arrow Is Pressed... glutFullScreen ( ); // Go Into Full Screen Mode break; case GLUT_KEY_DOWN: // When Down Arrow Is Pressed... glutReshapeWindow ( 500, 500 ); // Go Into A 500 By 500 Window break; default: break; } } void main ( int argc, char** argv ) // Create Main Function For Bringing It All Together { glutInit ( &argc, argv ); // Erm Just Write It =) init (); glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE ); // Display Mode glutInitWindowSize ( 500, 500 ); // If glutFullScreen wasn't called this is the window size glutCreateWindow ( "PolyVox OpenGL Example" ); // Window Title (argv[0] for current directory as title) //glutFullScreen ( ); // Put Into Full Screen glutDisplayFunc ( display ); // Matching Earlier Functions To Their Counterparts glutReshapeFunc ( reshape ); glutKeyboardFunc ( keyboard ); glutSpecialFunc ( arrow_keys ); #ifdef WIN32 //If we are on Windows we will need GLEW to access recent OpenGL functionality GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ cout << "Error: " << glewGetErrorString(err) << endl; } #endif //Make our volume contain a sphere in the center. uint16 minPos = 0; uint16 midPos = g_volData.getSideLength() / 2; uint16 maxPos = g_volData.getSideLength() - 1; createCube(Vector3DUint16(minPos, minPos, minPos), Vector3DUint16(maxPos, maxPos, maxPos), 0); createSphere(50.0f, 5); createSphere(40.0f, 4); createSphere(30.0f, 3); createSphere(20.0f, 2); createSphere(10.0f, 1); createCube(Vector3DUint16(minPos, minPos, minPos), Vector3DUint16(midPos, midPos, midPos), 0); createCube(Vector3DUint16(midPos, midPos, minPos), Vector3DUint16(maxPos, maxPos, midPos), 0); createCube(Vector3DUint16(midPos, minPos, midPos), Vector3DUint16(maxPos, midPos, maxPos), 0); createCube(Vector3DUint16(minPos, midPos, midPos), Vector3DUint16(midPos, maxPos, maxPos), 0); //Our volume is broken down into cuboid regions, and we create one mesh for each region. //This three-level for loop iterates over each region. for(uint16 uRegionZ = 0; uRegionZ < g_uVolumeSideLengthInRegions; ++uRegionZ) { for(uint16 uRegionY = 0; uRegionY < g_uVolumeSideLengthInRegions; ++uRegionY) { for(uint16 uRegionX = 0; uRegionX < g_uVolumeSideLengthInRegions; ++uRegionX) { //Create a new surface patch (which is basiaclly the PolyVox term for a mesh). IndexedSurfacePatch* ispCurrent = new IndexedSurfacePatch(); //Compute the extents of the current region //FIXME - This is a little complex? PolyVox could //provide more functions for dealing with regions? uint16 regionStartX = uRegionX * g_uRegionSideLength; uint16 regionStartY = uRegionY * g_uRegionSideLength; uint16 regionStartZ = uRegionZ * g_uRegionSideLength; uint16 regionEndX = regionStartX + g_uRegionSideLength; uint16 regionEndY = regionStartY + g_uRegionSideLength; uint16 regionEndZ = regionStartZ + g_uRegionSideLength; Vector3DInt32 regLowerCorner(regionStartX, regionStartY, regionStartZ); Vector3DInt32 regUpperCorner(regionEndX, regionEndY, regionEndZ); //Extract the surface for this region extractReferenceSurface(&g_volData, Region(regLowerCorner, regUpperCorner), ispCurrent); g_openGLSurfacePatches[uRegionX][uRegionY][uRegionZ] = BuildOpenGLSurfacePatch(*ispCurrent); delete ispCurrent; } } } glutMainLoop ( ); // Initialize The Main Loop }