2009-03-13 23:36:45 +00:00

273 lines
9.9 KiB
C++

#include "PolyVoxCore/BlockVolume.h"
#include "PolyVoxCore/IndexedSurfacePatch.h"
#include "PolyVoxCore/SurfaceExtractors.h"
#include "PolyVoxCore/Utility.h"
#include <windows.h> // Standard Header For Most Programs
#ifdef WIN32
#include "glew/glew.h"
#else
#include <gl/gl.h> // The GL Header File
#endif
#include <gl/glut.h> // The GL Utility Toolkit (Glut) Header
#include <iostream>
//Some namespaces we need
using namespace std;
using namespace PolyVox;
using namespace std;
//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<uint8> 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
IndexedSurfacePatch* g_ispRegionSurfaces[g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions];
GLuint indexBuffers[g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions];
GLuint vertexBuffers[g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions];
void createSphereInVolume(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);
}
else
{
g_volData.setVoxelAt(x,y,z, 0);
}
}
}
}
}
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)
{
const vector<uint32>& vecIndices = g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ]->getIndices();
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[uRegionX][uRegionY][uRegionZ]);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffers[uRegionX][uRegionY][uRegionZ]);
glEnableClientState(GL_VERTEX_ARRAY);
int s = vecIndices.size();
glDrawElements(GL_TRIANGLE_STRIP, s, GL_UNSIGNED_INT, 0);
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.
createSphereInVolume(50.0f, 1);
//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).
g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ] = new IndexedSurfacePatch();
IndexedSurfacePatch* ispCurrent = g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ];
//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);
const vector<SurfaceVertex>& vecVertices = g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ]->getVertices();
const vector<uint32>& vecIndices = g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ]->getIndices();
glGenBuffers(1, &(indexBuffers[uRegionX][uRegionY][uRegionZ]));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffers[uRegionX][uRegionY][uRegionZ]);
int s = vecIndices.size() * sizeof(GLint);
if(s != 0)
{
GLvoid* blah = (GLvoid*)(&(vecIndices[0]));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s, blah, GL_STATIC_DRAW);
}
glGenBuffers(1, &(vertexBuffers[uRegionX][uRegionY][uRegionZ]));
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[uRegionX][uRegionY][uRegionZ]);
glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(GLfloat) * 3, 0, GL_STATIC_DRAW);
GLfloat* ptr = (GLfloat*)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
for(vector<SurfaceVertex>::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 + v3dRegionOffset;
*ptr = v3dFinalVertexPos.getX();
ptr++;
*ptr = v3dFinalVertexPos.getY();
ptr++;
*ptr = v3dFinalVertexPos.getZ();
ptr++;
}
glUnmapBuffer(GL_ARRAY_BUFFER);
}
}
}
glutMainLoop ( ); // Initialize The Main Loop
//Delete all the surface patches we created.
for(uint16 uRegionZ = 0; uRegionZ < g_uVolumeSideLengthInRegions; ++uRegionZ)
{
for(uint16 uRegionY = 0; uRegionY < g_uVolumeSideLengthInRegions; ++uRegionY)
{
for(uint16 uRegionX = 0; uRegionX < g_uVolumeSideLengthInRegions; ++uRegionX)
{
delete g_ispRegionSurfaces[uRegionX][uRegionY][uRegionZ];
}
}
}
}