mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 05:17:02 +02:00
758 lines
28 KiB
C++
Executable file
758 lines
28 KiB
C++
Executable file
//
|
|
// Render.cpp
|
|
//
|
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
|
//
|
|
|
|
|
|
|
|
#include "svoviewer.h"
|
|
|
|
////////////////////////////////////////////////////
|
|
// Some GL helper functions
|
|
|
|
GLubyte SvoViewer::SetupGlVBO(GLuint * id, int sizeInBytes, GLenum target, GLenum usage, void * dataUp )
|
|
{
|
|
glGenBuffers(1, id);
|
|
glBindBuffer(target, *id);
|
|
glBufferData(target, sizeInBytes, dataUp, usage);
|
|
return PrintGLErrorCode();
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
// Include all the render methods here.
|
|
|
|
////////////////////////////////////////////////////
|
|
// Points system
|
|
|
|
bool SvoViewer::FindNumLeaves(OctreeElement* node, void* extraData)
|
|
{
|
|
FindNumLeavesData* args = (FindNumLeavesData*)extraData;
|
|
if (node->isLeaf()) args->numLeaves++;
|
|
return true;
|
|
}
|
|
|
|
struct PointRenderAssembleData
|
|
{
|
|
glm::vec3 * buffer;
|
|
unsigned char* colorBuffer;
|
|
unsigned int count;
|
|
};
|
|
|
|
bool SvoViewer::PointRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
|
|
{
|
|
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
|
|
PointRenderAssembleData* args = (PointRenderAssembleData*)extraData;
|
|
if (!node->isLeaf()) return true;
|
|
AABox box = voxel->getAABox();
|
|
glm::vec3 center = box.calcCenter();
|
|
center += glm::vec3(0, -.05, 0);
|
|
center *= 100.0;
|
|
args->buffer[args->count] = center;
|
|
int cCount = args->count * 3;
|
|
args->colorBuffer[cCount] = voxel->getTrueColor()[0];
|
|
args->colorBuffer[cCount+1] = voxel->getTrueColor()[1];
|
|
args->colorBuffer[cCount+2] = voxel->getTrueColor()[2];
|
|
args->count++;
|
|
return true; // keep going!
|
|
}
|
|
|
|
void SvoViewer::InitializePointRenderSystem()
|
|
{
|
|
qDebug("Initializing point render system!\n");
|
|
quint64 fstart = usecTimestampNow();
|
|
_renderFlags.voxelRenderDirty = true;
|
|
_renderFlags.voxelOptRenderDirty = true;
|
|
|
|
glGenBuffers(1, &_pointVtxBuffer);
|
|
glBindBuffer( GL_ARRAY_BUFFER, _pointVtxBuffer);
|
|
|
|
// Assemble the buffer data.
|
|
PointRenderAssembleData args;
|
|
args.buffer = _pointVertices = new glm::vec3[_nodeCount];
|
|
args.colorBuffer = _pointColors = new unsigned char[_nodeCount*3];
|
|
assert(args.buffer != NULL);
|
|
args.count = 0;
|
|
_systemTree.recurseTreeWithOperation(&PointRenderAssemblePerVoxel, &args);
|
|
|
|
assert(args.count < _nodeCount);
|
|
_pointVerticesCount = args.count;
|
|
|
|
// create the data store.
|
|
//int size = _nodeCount * sizeof(glm::vec3);
|
|
glBindBuffer( GL_ARRAY_BUFFER, _pointVtxBuffer);
|
|
glBufferData(GL_ARRAY_BUFFER, _nodeCount * 3, args.buffer, GL_STATIC_DRAW);
|
|
//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
|
|
|
|
//glBindBuffer(GL_ARRAY_BUFFER, _pointColorBuffer);
|
|
//glBufferData(GL_ARRAY_BUFFER, size, args.colorBuffer, GL_STATIC_DRAW);
|
|
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
|
|
|
|
_renderFlags.ptRenderDirty = false;
|
|
_ptRenderInitialized = true;
|
|
float elapsed = (float)(usecTimestampNow() - fstart) / 1000.f;
|
|
qDebug("Point render intialization took %f time for %ld nodes\n", elapsed, _nodeCount);
|
|
}
|
|
|
|
void SvoViewer::RenderTreeSystemAsPoints()
|
|
{
|
|
|
|
glDisable(GL_LIGHTING);
|
|
glEnable(GL_DEPTH_TEST);
|
|
//glDisable(GL_DEPTH_TEST);
|
|
|
|
glColor3f(.5, 0, 0);
|
|
glPointSize(1.0f);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableVertexAttribArray(0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _pointVtxBuffer);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
|
|
glVertexPointer(3, GL_FLOAT, 0, _pointVertices);
|
|
|
|
// Removed for testing.
|
|
/*glEnableClientState(GL_COLOR_ARRAY);
|
|
//glEnableVertexAttribArray(1);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _pointColorBuffer);*/
|
|
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
|
|
//glColorPointer(3, GL_UNSIGNED_BYTE, 0, _pointColors);
|
|
|
|
glDrawArrays(GL_POINTS, 0, _pointVerticesCount);
|
|
}
|
|
|
|
void SvoViewer::StopUsingPointRenderSystem()
|
|
{
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glDisableVertexAttribArray(0);
|
|
glDisableVertexAttribArray(1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
|
|
const GLchar * simpleVtxShaderSrc =
|
|
"uniform mat4 viewMatrix;\n\
|
|
uniform mat4 projectionMatrix;\n\
|
|
uniform mat4 modelMatrix;\n\
|
|
void main()\n\
|
|
{\n\
|
|
vec4 world_pos = modelMatrix * gl_Vertex;\n\
|
|
vec4 view_pos = viewMatrix * world_pos;\n\
|
|
gl_Position = projectionMatrix * view_pos;\n\
|
|
gl_FrontColor = gl_Color;\n\
|
|
}\n";
|
|
int simpleVtxShaderLen = strlen(simpleVtxShaderSrc);
|
|
|
|
const GLchar * simpleGeomShaderSrc =
|
|
"#version 330 compatibility\n\
|
|
layout(triangles) in;\n\
|
|
layout(triangle_strip, max_vertices=3) out;\n\
|
|
void main()\n\
|
|
{\n\
|
|
for(int i=0; i<3; i++)\n\
|
|
{\n\
|
|
gl_Position = gl_in[i].gl_Position;\n\
|
|
EmitVertex();\n\
|
|
}\n\
|
|
EndPrimitive();\n\
|
|
}";
|
|
int simpleGeomShaderLen = strlen(simpleGeomShaderSrc);
|
|
|
|
const GLchar * simpleFragShaderSrc =
|
|
"void main()\n\
|
|
{\n\
|
|
gl_FragColor = gl_Color;\n\
|
|
}\n";
|
|
int simpleFragShaderLen = strlen(simpleFragShaderSrc);
|
|
|
|
struct VoxelRenderAssembleData
|
|
{
|
|
glm::vec3 * buffer;
|
|
unsigned char* colorBuffer;
|
|
GLuint** idxBuffs;
|
|
unsigned int leafCount;
|
|
unsigned int lastBufferSegmentStart;
|
|
GLuint vtxID;
|
|
GLuint colorID;
|
|
GLuint* idxIds;
|
|
|
|
};
|
|
|
|
bool SvoViewer::VoxelRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
|
|
{
|
|
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
|
|
VoxelRenderAssembleData* args = (VoxelRenderAssembleData*)extraData;
|
|
if (!node->isLeaf()) return true;
|
|
|
|
AABox box = voxel->getAABox();
|
|
int totalNodesProcessedSinceLastFlush = args->leafCount - args->lastBufferSegmentStart;
|
|
// ack, one of these components is flags, not alpha
|
|
int cCount = totalNodesProcessedSinceLastFlush * 4; // Place it relative to the current segment.
|
|
unsigned char col[4] = {voxel->getTrueColor()[0], voxel->getTrueColor()[1], voxel->getTrueColor()[2], 1};
|
|
for(int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
|
|
memcpy(&args->colorBuffer[cCount+i*4], col, sizeof(col));
|
|
|
|
int vCount = totalNodesProcessedSinceLastFlush * GLOBAL_NORMALS_VERTICES_PER_VOXEL; // num vertices we've added so far.
|
|
{
|
|
int idxCount = totalNodesProcessedSinceLastFlush * INDICES_PER_FACE; // same for every cube face.
|
|
// Indices follow a static pattern.
|
|
for (int i = 0; i < NUM_CUBE_FACES; i++)
|
|
{
|
|
GLuint* idxBuff = args->idxBuffs[i];
|
|
for (int j = 0; j < INDICES_PER_FACE; j++)
|
|
{
|
|
idxBuff[idxCount+j] = SvoViewerNames::cubeFaceIndices[i][j] + vCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Grab vtx positions according to setup from indices layout above.
|
|
//center += glm::vec3(0, -.05, 0);
|
|
float scale = 100.0;
|
|
for ( int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
|
|
{
|
|
args->buffer[vCount] = box.getVertex((BoxVertex)i); // i corresponds to BOTTOM_LEFT_NEAR,BOTTOM_RIGHT_NEAR,...
|
|
args->buffer[vCount++] *= scale;
|
|
}
|
|
|
|
args->leafCount++;
|
|
totalNodesProcessedSinceLastFlush++;
|
|
|
|
if (totalNodesProcessedSinceLastFlush >= REASONABLY_LARGE_BUFFER) // Flush data to GL once we have assembled enough of it.
|
|
{
|
|
//qDebug("committing!\n");
|
|
PrintGLErrorCode();
|
|
glBindBuffer(GL_ARRAY_BUFFER, args->vtxID);
|
|
glBufferSubData(GL_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(glm::vec3) * GLOBAL_NORMALS_VERTICES_PER_VOXEL,
|
|
totalNodesProcessedSinceLastFlush * sizeof(glm::vec3) * GLOBAL_NORMALS_VERTICES_PER_VOXEL, args->buffer);
|
|
PrintGLErrorCode();
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, args->colorID);
|
|
glBufferSubData(GL_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(GLubyte) * 4 * GLOBAL_NORMALS_VERTICES_PER_VOXEL,
|
|
totalNodesProcessedSinceLastFlush * sizeof(GLubyte) * 4 * GLOBAL_NORMALS_VERTICES_PER_VOXEL, args->colorBuffer);
|
|
PrintGLErrorCode();
|
|
|
|
for (int i = 0; i < NUM_CUBE_FACES; i++)
|
|
{
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, args->idxIds[i]);
|
|
GLuint* idxBuff = args->idxBuffs[i];
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, args->lastBufferSegmentStart * sizeof(GLuint) * INDICES_PER_FACE,
|
|
totalNodesProcessedSinceLastFlush * sizeof(GLuint) * INDICES_PER_FACE, idxBuff); // Rework.
|
|
PrintGLErrorCode();
|
|
}
|
|
glFlush();
|
|
PrintGLErrorCode();
|
|
args->lastBufferSegmentStart = args->leafCount;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SvoViewer::InitializeVoxelRenderSystem()
|
|
{
|
|
qDebug("Initializing voxel render system.\n");
|
|
|
|
FindNumLeavesData data;
|
|
data.numLeaves = 0;
|
|
_systemTree.recurseTreeWithOperation(&FindNumLeaves, &data);
|
|
_leafCount = data.numLeaves;
|
|
|
|
glGenBuffers(NUM_CUBE_FACES, _vboIndicesIds);
|
|
for (int i = 0; i < NUM_CUBE_FACES; i++)
|
|
{
|
|
_vboIndices[i] = new GLuint[REASONABLY_LARGE_BUFFER * INDICES_PER_FACE];
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[i]);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_FACE * sizeof(GLuint) * _nodeCount,
|
|
NULL, GL_STATIC_DRAW);
|
|
}
|
|
|
|
glGenBuffers(1, &_vboVerticesID);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
|
glBufferData(GL_ARRAY_BUFFER, GLOBAL_NORMALS_VERTICES_PER_VOXEL * sizeof(glm::vec3) * _nodeCount, NULL, GL_STATIC_DRAW);
|
|
PrintGLErrorCode();
|
|
|
|
glGenBuffers(1, &_vboColorsID);
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
|
glBufferData(GL_ARRAY_BUFFER, GLOBAL_NORMALS_VERTICES_PER_VOXEL * sizeof(GLubyte) * 4 * _nodeCount, NULL, GL_STATIC_DRAW);
|
|
PrintGLErrorCode();
|
|
|
|
//int numVertices = GLOBAL_NORMALS_VERTICES_PER_VOXEL * _nodeCount;
|
|
// Working set test = dead after about 1.5gb! I.e... don't try to allocate this here.
|
|
// This will tell you about what you have to play with. On my system, it died consistently after 1.3-1.5gb.
|
|
//glm::vec3* memPtrs[20];
|
|
//int i;
|
|
//for (i = 0; i < 20; i++)
|
|
//{
|
|
// memPtrs[i] = new glm::vec3[numVertices / 20];
|
|
//}
|
|
//
|
|
//for (i = 0; i < 20; i++)
|
|
// delete [] memPtrs[i];
|
|
#define PAD 32
|
|
_readVerticesArray = new glm::vec3[GLOBAL_NORMALS_VERTICES_PER_VOXEL * REASONABLY_LARGE_BUFFER + PAD];
|
|
_readColorsArray = new GLubyte[(GLOBAL_NORMALS_VERTICES_PER_VOXEL * REASONABLY_LARGE_BUFFER * 4) + PAD];
|
|
|
|
// Assemble the buffer data.
|
|
VoxelRenderAssembleData args;
|
|
args.buffer = _readVerticesArray;
|
|
args.colorBuffer = _readColorsArray;
|
|
assert(args.buffer != NULL && args.colorBuffer != NULL);
|
|
args.leafCount = 0;
|
|
args.lastBufferSegmentStart = 0;
|
|
args.idxIds = _vboIndicesIds;
|
|
args.idxBuffs = _vboIndices;
|
|
args.vtxID = _vboVerticesID;
|
|
args.colorID = _vboColorsID;
|
|
_systemTree.recurseTreeWithOperation(&VoxelRenderAssemblePerVoxel, &args);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), 0);
|
|
glEnableVertexAttribArray(0);
|
|
//glVertexPointer(3, GL_FLOAT, 0, 0);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
|
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(unsigned char) * 4, 0);
|
|
glEnableVertexAttribArray(1);
|
|
|
|
// Set up the shaders.
|
|
_vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
_geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
|
|
_pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
glShaderSource(_vertexShader, 1, &simpleVtxShaderSrc, &simpleVtxShaderLen);
|
|
glShaderSource(_geometryShader, 1, &simpleGeomShaderSrc, &simpleGeomShaderLen);
|
|
glShaderSource(_pixelShader, 1, &simpleFragShaderSrc, &simpleFragShaderLen);
|
|
|
|
GLchar shaderLog[1000];
|
|
GLsizei shaderLogLength;
|
|
//GLint compiled;
|
|
glCompileShaderARB((GLhandleARB)_vertexShader);
|
|
glGetShaderInfoLog(_vertexShader, 1000, &shaderLogLength, shaderLog);
|
|
if (shaderLog[0] != 0) qDebug("Shaderlog v :\n %s\n", shaderLog);
|
|
glCompileShaderARB((GLhandleARB)_geometryShader);
|
|
glGetShaderInfoLog(_geometryShader, 1000, &shaderLogLength, shaderLog);
|
|
if (shaderLog[0] != 0) qDebug("Shaderlog g :\n %s\n", shaderLog);
|
|
glCompileShaderARB((GLhandleARB)_pixelShader);
|
|
glGetShaderInfoLog(_pixelShader, 51000, &shaderLogLength, shaderLog);
|
|
if (shaderLog[0] != 0) qDebug("Shaderlog p :\n %s\n", shaderLog);
|
|
|
|
_linkProgram = glCreateProgram();
|
|
glAttachShader(_linkProgram, _vertexShader);
|
|
glAttachShader(_linkProgram, _geometryShader);
|
|
glAttachShader(_linkProgram, _pixelShader);
|
|
glLinkProgram(_linkProgram);
|
|
GLint linked;
|
|
glGetProgramiv(_linkProgram, GL_LINK_STATUS, &linked);
|
|
if (!linked) qDebug("Linking failed! %d\n", linked);
|
|
|
|
|
|
_voxelOptRenderInitialized = true;
|
|
}
|
|
|
|
void SvoViewer::RenderTreeSystemAsVoxels()
|
|
{
|
|
if (!_voxelOptRenderInitialized) return; // What the??
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
// for performance, enable backface culling
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
// disable specular lighting for ground and voxels
|
|
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
|
|
|
|
setupWorldLight();
|
|
|
|
glNormal3f(0,1.0f,0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_TOP]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
glNormal3f(0,-1.0f,0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_BOTTOM]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
glNormal3f(-1.0f,0,0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_LEFT]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
glNormal3f(1.0f,0,0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_RIGHT]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
glNormal3f(0,0,-1.0f);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_FRONT]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
glNormal3f(0,0,1.0f);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesIds[CUBE_BACK]);
|
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _leafCount - 1,
|
|
INDICES_PER_FACE * _leafCount, GL_UNSIGNED_INT, 0);
|
|
|
|
//glDisable(GL_CULL_FACE);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
void SvoViewer::StopUsingVoxelRenderSystem()
|
|
{
|
|
if (!_voxelOptRenderInitialized) return;
|
|
//for (int i = 0; i < NUM_CUBE_FACES; i++) delete [] _vboIndices[i];
|
|
//delete [] _readVerticesArray;
|
|
//delete [] _readColorsArray;
|
|
|
|
glDeleteBuffers(1, &_vboVerticesID);
|
|
glDeleteBuffers(1, &_vboColorsID);
|
|
glDeleteBuffers(NUM_CUBE_FACES, _vboIndicesIds);
|
|
_voxelOptRenderInitialized = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
|
|
|
|
// NOTE - the voxel tree data structure doesn't currently have neighbor information encoded in any convenient searchable
|
|
// way. So here I'm using a HUGE hack to compute repeated faces in the data structure. I 1) compute the barycentric coordinates
|
|
// of every triangle for every leaf face. 2) Compute the center point for the triangle. (edit: I removed the barycentric part after some testing),
|
|
// 3) That center point becomes a ID for that tri.
|
|
// 4) If that ID is added more than once, discard this triangle, as it must be an internal. External triangles will only be added once.
|
|
// SUBNOTE - I have NO idea how the tree traversal is actually happening as I haven't looked into its usage pattern. If traversal order was intelligent,
|
|
// static and the pattern was predetermined, traversal could actually generate a mostly sorted list automatically. Because that isn't guaranteed
|
|
// here, its uglier.
|
|
|
|
|
|
// The
|
|
bool SvoViewer::TrackVisibleFaces(OctreeElement* node, void* extraData)
|
|
{
|
|
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
|
|
VisibleFacesData* args = (VisibleFacesData*)extraData;
|
|
if (!node->isLeaf()) return true;
|
|
|
|
AABox box = voxel->getAABox();
|
|
glm::vec3 p0, p1, p2, p3, hackCenterVal;
|
|
glm::vec3 cubeVerts[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
|
|
|
|
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++) // Cache as aabox reconstructs with every call.
|
|
cubeVerts[i] = box.getVertex((BoxVertex)i);
|
|
|
|
for (int i = 0; i < NUM_CUBE_FACES; i++)
|
|
{
|
|
p0 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][0]];
|
|
p1 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][1]];
|
|
p2 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][2]];
|
|
p3 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][3]];
|
|
hackCenterVal = computeQuickAndDirtyQuadCenter(p0, p1, p2, p3);
|
|
args->ptList[args->count] += hackCenterVal;
|
|
args->count++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Do a binary search on the vector list.
|
|
int SvoViewer::binVecSearch(glm::vec3 searchVal, glm::vec3* list, int count, int * found)
|
|
{
|
|
int key = count / 2;
|
|
int lowerbound = 0;
|
|
int upperbound = count-1;
|
|
int lastkey = -1;
|
|
while (lastkey != key)
|
|
{
|
|
lastkey = key;
|
|
int ret = ptCloseEnough(&searchVal, &list[key]);
|
|
if (ret == 0) { *found = 1; return key; }
|
|
if (lowerbound == key || upperbound == key) { *found = 0; return key;} // closest we can get!
|
|
if (ret < 0)
|
|
{
|
|
upperbound = key;
|
|
if (key == lowerbound+1) key = lowerbound;
|
|
else key = ((upperbound - lowerbound) / 2) + lowerbound;
|
|
}
|
|
else if (ret > 0)
|
|
{
|
|
lowerbound = key;
|
|
if (key == upperbound-1) key = upperbound;
|
|
else key = ((upperbound - lowerbound) /2) + lowerbound;
|
|
}
|
|
}
|
|
*found = 0;
|
|
return key;
|
|
}
|
|
|
|
struct VoxelOptRenderAssembleData
|
|
{
|
|
Vertex* vtxBuffer;
|
|
GLuint* idxBuffer;
|
|
int vtxCount;
|
|
int idxCount;
|
|
AABoundingVolume bounds;
|
|
int faceCenterCount;
|
|
glm::vec3 * faceCenterList;
|
|
int discardedCount;
|
|
int elemCount;
|
|
};
|
|
|
|
bool SvoViewer::VoxelOptRenderAssemblePerVoxel(OctreeElement* node, void* extraData)
|
|
{
|
|
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
|
|
VoxelOptRenderAssembleData* args = (VoxelOptRenderAssembleData*)extraData;
|
|
if (!node->isLeaf()) return true;
|
|
|
|
AABox box = voxel->getAABox();
|
|
|
|
glm::vec3 p0, p1, p2, p3, hackCenterVal;
|
|
glm::vec3 cubeVerts[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
|
|
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++) // Cache, as aabox reconstructs with every call.
|
|
cubeVerts[i] = box.getVertex((BoxVertex)i);
|
|
|
|
bool doAddFace[NUM_CUBE_FACES] = {true, false, true, true, true, true}; // Cull bottom faces by default.
|
|
|
|
// Accumulate all the faces that need to be added.
|
|
for (int i = 0; i < NUM_CUBE_FACES; i++)
|
|
{
|
|
p0 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][0]];
|
|
p1 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][1]];
|
|
p2 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][2]];
|
|
p3 = cubeVerts[SvoViewerNames::cubeFaceVtxs[i][3]];
|
|
hackCenterVal = computeQuickAndDirtyQuadCenter(p0, p1, p2, p3);
|
|
// Search for this in the face center list
|
|
//glm::vec3 * foundVal = (glm::vec3*)bsearch(&hackCenterVal, args->faceCenterList, args->faceCenterCount, sizeof(glm::vec3), ptCompFunc);
|
|
int foundBVS = 0;
|
|
int idxIntoList = binVecSearch(hackCenterVal, args->faceCenterList, args->faceCenterCount, &foundBVS);
|
|
if (foundBVS == 0) { assert(0); continue; } // What the?
|
|
//if (foundVal == NULL) { assert(0); continue; } // What the?
|
|
//unsigned int idxIntoList = (foundVal - args->faceCenterList) / sizeof(glm::vec3);
|
|
assert(idxIntoList <= args->faceCenterCount-1);
|
|
// Now check face center list values that are immmediately adjacent to this value. If they're equal, don't add this face as
|
|
// another leaf voxel must contain this triangle too.
|
|
bool foundMatch = false;
|
|
if (idxIntoList != 0)
|
|
{
|
|
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList-1]) == 0) foundMatch = true;
|
|
}
|
|
if (idxIntoList != args->faceCenterCount-1 && foundMatch == false)
|
|
{
|
|
if (ptCloseEnough(&hackCenterVal, &args->faceCenterList[idxIntoList+1]) == 0) foundMatch = true;
|
|
}
|
|
if (foundMatch)
|
|
{
|
|
doAddFace[i] = false; // Remove.
|
|
args->discardedCount++;
|
|
}
|
|
}
|
|
|
|
#define VTX_NOT_USED 255
|
|
unsigned char vtxToAddMap[GLOBAL_NORMALS_VERTICES_PER_VOXEL]; // Map from vertex to actual position in the new vtx list.
|
|
memset(vtxToAddMap, VTX_NOT_USED, sizeof(vtxToAddMap));
|
|
|
|
// Figure out what vertices to add. NOTE - QUICK and dirty. easy opt - precalulate bit pattern for every face and just & it.
|
|
bool useVtx[GLOBAL_NORMALS_VERTICES_PER_VOXEL];
|
|
memset(useVtx, 0, sizeof(useVtx));
|
|
for ( int face = 0; face < NUM_CUBE_FACES; face++) // Vertex add order.
|
|
{
|
|
if (doAddFace[face])
|
|
{
|
|
for (int vOrder = 0; vOrder < 4; vOrder++)
|
|
{
|
|
useVtx[SvoViewerNames::cubeFaceVtxs[face][vOrder]] = true;
|
|
}
|
|
}
|
|
}
|
|
unsigned char vtxAddedCount = 0;
|
|
int baseVtxCount = args->vtxCount;
|
|
for (int i = 0; i < GLOBAL_NORMALS_VERTICES_PER_VOXEL; i++)
|
|
{
|
|
if (useVtx[i])
|
|
{
|
|
vtxToAddMap[i] = vtxAddedCount;
|
|
vtxAddedCount++;
|
|
|
|
args->vtxBuffer[args->vtxCount].position = cubeVerts[i];
|
|
args->vtxBuffer[args->vtxCount].position *= 100;
|
|
args->vtxBuffer[args->vtxCount].position.x -= 25;
|
|
args->vtxBuffer[args->vtxCount].position.y -= 4;
|
|
args->vtxBuffer[args->vtxCount].color[0] = voxel->getTrueColor()[0];
|
|
args->vtxBuffer[args->vtxCount].color[1] = voxel->getTrueColor()[1];
|
|
args->vtxBuffer[args->vtxCount].color[2] = voxel->getTrueColor()[2];
|
|
args->vtxBuffer[args->vtxCount].color[3] = 1;
|
|
args->bounds.AddToSet(args->vtxBuffer[args->vtxCount].position);
|
|
args->vtxCount++;
|
|
}
|
|
}
|
|
|
|
// Assemble the index lists.
|
|
for ( int face = 0; face < NUM_CUBE_FACES; face++)
|
|
{
|
|
if (doAddFace[face])
|
|
{
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
// index is : current vtx + offset in map table
|
|
args->idxBuffer[args->idxCount] = baseVtxCount + vtxToAddMap[ SvoViewerNames::cubeFaceIndices[face][i] ];
|
|
args->idxCount++;
|
|
args->elemCount += 2; // Add 2 elements per face
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SvoViewer::InitializeVoxelOptRenderSystem()
|
|
{
|
|
if (_voxelOptRenderInitialized) return;
|
|
_numSegments = 0;
|
|
_totalPossibleElems = 0;
|
|
memset(_numChildNodeLeaves, 0, sizeof(_numChildNodeLeaves));
|
|
memset(_segmentNodeReferences, 0, sizeof(_segmentNodeReferences));
|
|
memset(_segmentBoundingVolumes, 0, sizeof(_segmentBoundingVolumes));
|
|
|
|
// Set up the segments. Find the number of leaves at each subtree.
|
|
OctreeElement * rootNode = _systemTree.getRoot();
|
|
OctreeElement* node0fromRoot = rootNode->getChildAtIndex(0); // ALL the interesting data for our test SVO is in this node! HACK!!
|
|
//int rootNumChildren = rootNode->getChildCount();
|
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++)
|
|
{
|
|
OctreeElement* childNode1stOrder = node0fromRoot->getChildAtIndex(i);
|
|
if (childNode1stOrder == NULL) continue;
|
|
// Grab 2nd order nodes for better separation. At some point, this would need to be done intelligently.
|
|
for (int j = 0; j < NUMBER_OF_CHILDREN; j++)
|
|
{
|
|
OctreeElement* childNode2ndOrder = childNode1stOrder->getChildAtIndex(j);
|
|
if (childNode2ndOrder == NULL) continue;
|
|
|
|
//int num2ndOrderChildren = childNode2ndOrder->getChildCount();
|
|
// Figure out how populated this child is.
|
|
FindNumLeavesData data;
|
|
data.numLeaves = 0;
|
|
_systemTree.recurseNodeWithOperation(childNode2ndOrder, &FindNumLeaves, &data, 0);
|
|
|
|
// Some of these nodes have a single leaf. Ignore for the moment. We really only want the big segments in this test. Add this back in at some point.
|
|
if (data.numLeaves > 1)
|
|
{
|
|
_numChildNodeLeaves[_numSegments] = data.numLeaves;
|
|
_segmentNodeReferences[_numSegments] = childNode2ndOrder;
|
|
_totalPossibleElems += data.numLeaves * NUM_CUBE_FACES * 2;
|
|
_numSegments++;
|
|
qDebug("child node %d %d has %d leaves and %d children itself\n", i, j, data.numLeaves, childNode2ndOrder->getChildCount());
|
|
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("Out of segment space??? What the?\n"); break; }
|
|
}
|
|
}
|
|
if (_numSegments >= MAX_NUM_OCTREE_PARTITIONS ) { qDebug("Out of segment space??? What the?\n"); break; }
|
|
}
|
|
|
|
|
|
// Set up the VBO's. Once for every partition we stored.
|
|
for (int i = 0; i < _numSegments; i++)
|
|
{
|
|
// compute the visible set of this segment first.
|
|
glm::vec3* faceCenters = new glm::vec3[NUM_CUBE_FACES *_numChildNodeLeaves[i]];
|
|
VisibleFacesData visFaceData;
|
|
visFaceData.ptList = faceCenters;
|
|
visFaceData.count = 0;
|
|
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &TrackVisibleFaces, &visFaceData, 0);
|
|
// Now there's a list of all the face centers. Sort it.
|
|
qsort(faceCenters, visFaceData.count, sizeof(glm::vec3), ptCompFunc);
|
|
qDebug("Creating VBO's. Sorted neighbor list %d\n", i);
|
|
|
|
_readVertexStructs = new Vertex[GLOBAL_NORMALS_VERTICES_PER_VOXEL * _numChildNodeLeaves[i]];
|
|
_readIndicesArray = new GLuint[NUM_CUBE_FACES * 2 * 3 * _numChildNodeLeaves[i]];
|
|
|
|
VoxelOptRenderAssembleData args;
|
|
args.vtxBuffer = _readVertexStructs;
|
|
args.idxBuffer = _readIndicesArray;
|
|
args.vtxCount = 0;
|
|
args.idxCount = 0;
|
|
args.faceCenterCount = visFaceData.count;
|
|
args.faceCenterList = visFaceData.ptList;
|
|
args.discardedCount = 0;
|
|
args.elemCount = 0;
|
|
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &VoxelOptRenderAssemblePerVoxel, &args, 0);
|
|
_segmentBoundingVolumes[i] = args.bounds;
|
|
_segmentElemCount[i] = args.elemCount;
|
|
|
|
SetupGlVBO(&_vboOVerticesIds[i], args.vtxCount * sizeof(Vertex), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readVertexStructs);
|
|
SetupGlVBO(&_vboOIndicesIds[i], args.idxCount * sizeof(GLuint), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readIndicesArray);
|
|
|
|
qDebug("Partition %d, vertices %d, indices %d, discarded %d\n", i, args.vtxCount, args.idxCount, args.discardedCount);
|
|
|
|
delete [] _readVertexStructs;
|
|
delete [] _readIndicesArray;
|
|
delete [] faceCenters;
|
|
}
|
|
|
|
_voxelOptRenderInitialized = true;
|
|
}
|
|
|
|
void SvoViewer::RenderTreeSystemAsOptVoxels()
|
|
{
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
// disable specular lighting for ground and voxels
|
|
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
|
|
|
|
setupWorldLight();
|
|
_numElemsDrawn = 0;
|
|
for (int i = 0; i < _numSegments; i++)
|
|
{
|
|
if (_displayOnlyPartition == i || _displayOnlyPartition == NO_PARTITION )
|
|
{
|
|
if (isVisibleBV(&_segmentBoundingVolumes[i], &_myCamera, &_viewFrustum)) // Add aggressive LOD check here.
|
|
{
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vboOVerticesIds[i]);
|
|
// NOTE: mac compiler doesn't support offsetof() for non-POD types, which apparently glm::vec3 is
|
|
//glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
|
|
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
|
|
glEnableVertexAttribArray(ATTRIB_POSITION);
|
|
|
|
// NOTE: mac compiler doesn't support offsetof() for non-POD types, which apparently glm::vec3 is
|
|
//glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,color));
|
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), (void*)sizeof(glm::vec3));
|
|
glEnableVertexAttribArray(ATTRIB_COLOR);
|
|
|
|
//glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex,position));
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
// NOTE: mac compiler doesn't support offsetof() for non-POD types, which apparently glm::vec3 is
|
|
//glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)offsetof(Vertex,color));
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)sizeof(glm::vec3));
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboOIndicesIds[i]);
|
|
|
|
glDrawElements(GL_TRIANGLES, _segmentElemCount[i], GL_UNSIGNED_INT, NULL);
|
|
_numElemsDrawn += _segmentElemCount[i];
|
|
|
|
/*glColor3f(.8f, 0.0f, 0.0f);
|
|
glBegin(GL_POINTS);
|
|
for (int j = 0; j < GLOBAL_NORMALS_VERTICES_PER_VOXEL; j++)
|
|
{
|
|
glm::vec3 pt = _segmentBoundingVolumes[i].getCorner((BoxVertex)j);
|
|
glVertex3f(pt.x, pt.y, pt.z);
|
|
}
|
|
glEnd();*/
|
|
}
|
|
}
|
|
}
|
|
glDisableVertexAttribArray(ATTRIB_POSITION);
|
|
glDisableVertexAttribArray(ATTRIB_COLOR);
|
|
}
|
|
|
|
|
|
void SvoViewer::StopUsingVoxelOptRenderSystem()
|
|
{
|
|
glDisableVertexAttribArray(ATTRIB_POSITION);
|
|
glDisableVertexAttribArray(ATTRIB_COLOR);
|
|
glDisable(GL_LIGHTING);
|
|
}
|
|
|
|
|