370 lines
12 KiB
C++
370 lines
12 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;
|
|
|
|
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<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
|
|
OpenGLSurfacePatch g_openGLSurfacePatches[g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions][g_uVolumeSideLengthInRegions];
|
|
|
|
OpenGLSurfacePatch BuildOpenGLSurfacePatch(IndexedSurfacePatch& isp)
|
|
{
|
|
OpenGLSurfacePatch result;
|
|
|
|
const vector<SurfaceVertex>& vecVertices = isp.getVertices();
|
|
const vector<uint32>& 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) * 9, 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 + static_cast<Vector3DFloat>(isp.m_v3dRegionPosition);
|
|
|
|
*ptr = v3dFinalVertexPos.getX();
|
|
ptr++;
|
|
*ptr = v3dFinalVertexPos.getY();
|
|
ptr++;
|
|
*ptr = v3dFinalVertexPos.getZ();
|
|
ptr++;
|
|
|
|
*ptr = vertex.getNormal().getX();
|
|
ptr++;
|
|
*ptr = vertex.getNormal().getY();
|
|
ptr++;
|
|
*ptr = vertex.getNormal().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);
|
|
|
|
glEnable(GL_LIGHTING);
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
|
glEnable(GL_LIGHT0);
|
|
|
|
// Create light components
|
|
/*GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
|
|
GLfloat diffuseLight[] = { 0.8f, 0.8f, 0.8, 1.0f };
|
|
GLfloat specularLight[] = { 0.5f, 0.5f, 0.5f, 1.0f };
|
|
GLfloat position[] = { -1.5f, 1.0f, -4.0f, 0.0f };
|
|
|
|
// Assign created components to GL_LIGHT0
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
|
|
glLightfv(GL_LIGHT0, GL_POSITION, position);*/
|
|
|
|
}
|
|
|
|
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, 36, 0);
|
|
glNormalPointer(GL_FLOAT, 36, (GLvoid*)12);
|
|
glColorPointer(3, GL_FLOAT, 36, (GLvoid*)24);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_openGLSurfacePatches[uRegionX][uRegionY][uRegionZ].indexBuffer);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_NORMAL_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_NORMAL_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 =)
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
init ();
|
|
|
|
glutMainLoop ( ); // Initialize The Main Loop
|
|
}
|
|
|