mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 13:00:50 +02:00
384 lines
15 KiB
C++
Executable file
384 lines
15 KiB
C++
Executable file
//
|
|
// Render2.cpp
|
|
//
|
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
|
//
|
|
|
|
|
|
#include "svoviewer.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Helper functions
|
|
|
|
// Precision dependent hack. After debugging - change this to a magnitude function.
|
|
// simple version for clarity/debugging.
|
|
int SvoViewer::ptCompFunc(const void * a, const void * b)
|
|
{
|
|
if ((*(glm::vec3*)a).x < (*(glm::vec3*)b).x) return -1;
|
|
else if ((*(glm::vec3*)a).x > (*(glm::vec3*)b).x) return 1;
|
|
|
|
if ((*(glm::vec3*)a).y < (*(glm::vec3*)b).y) return -1;
|
|
else if ((*(glm::vec3*)a).y > (*(glm::vec3*)b).y) return 1;
|
|
|
|
if ((*(glm::vec3*)a).z < (*(glm::vec3*)b).z) return -1;
|
|
else if ((*(glm::vec3*)a).z > (*(glm::vec3*)b).z) return 1;
|
|
return 0;
|
|
}
|
|
|
|
//#define PRECISION_ERR .00000001
|
|
#define PRECISION_ERR .00001
|
|
// aggressive mode
|
|
//(0.00097656250 /2) // Space of smallest voxel should define our error bounds here. Test this if time allows.
|
|
int SvoViewer::ptCloseEnough(const void * a, const void * b)
|
|
{
|
|
glm::vec3 diffVec = (*(glm::vec3*)a) - (*(glm::vec3*)b);
|
|
if (fabs(diffVec.x) < PRECISION_ERR && fabs(diffVec.y) < PRECISION_ERR && fabs(diffVec.z) < PRECISION_ERR) return 0;
|
|
//float len = diffVec.length();
|
|
//if (len < PRECISION_ERR) return 0;
|
|
if ((*(glm::vec3*)a).x < (*(glm::vec3*)b).x) return -1;
|
|
else if ((*(glm::vec3*)a).x > (*(glm::vec3*)b).x) return 1;
|
|
if ((*(glm::vec3*)a).y < (*(glm::vec3*)b).y) return -1;
|
|
else if ((*(glm::vec3*)a).y > (*(glm::vec3*)b).y) return 1;
|
|
if ((*(glm::vec3*)a).z < (*(glm::vec3*)b).z) return -1;
|
|
else if ((*(glm::vec3*)a).z > (*(glm::vec3*)b).z) return 1;
|
|
return 0;
|
|
}
|
|
|
|
// return parameterized intersection in t.
|
|
bool SvoViewer::parameterizedRayPlaneIntersection(const glm::vec3 origin, const glm::vec3 direction, const glm::vec3 planePt, const glm::vec3 planeNormal, float *t)
|
|
{
|
|
float denom = glm::dot(direction, planeNormal);
|
|
if (denom < PRECISION_ERR) return false;
|
|
|
|
glm::vec3 p010 = planePt - origin;
|
|
*t = glm::dot(p010, planeNormal) / denom;
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// 2nd stab at optimizing this. Cull back faces more aggressively.
|
|
|
|
|
|
struct VoxelOpt2RenderAssembleData
|
|
{
|
|
Vertex* vtxBuffer;
|
|
VoxelDimIdxSet* idxSet;
|
|
int vtxCount;
|
|
int faceCenterCount;
|
|
glm::vec3 * faceCenterList;
|
|
int discardedCount;
|
|
};
|
|
|
|
bool SvoViewer::VoxelOpt2RenderAssemblePerVoxel(OctreeElement* node, void* extraData)
|
|
{
|
|
VoxelTreeElement* voxel = (VoxelTreeElement*)node;
|
|
VoxelOpt2RenderAssembleData* args = (VoxelOpt2RenderAssembleData*)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);
|
|
// BSEARCH FAILING! What the? wrote my own index approximate version.
|
|
int foundBVS = 0;
|
|
int idxIntoList = binVecSearch(hackCenterVal, args->faceCenterList, args->faceCenterCount, &foundBVS);
|
|
if (foundBVS == 0) { assert(0); continue; } // What the?
|
|
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->getColor()[0];
|
|
args->vtxBuffer[args->vtxCount].color[1] = voxel->getColor()[1];
|
|
args->vtxBuffer[args->vtxCount].color[2] = voxel->getColor()[2];
|
|
args->vtxBuffer[args->vtxCount].color[3] = 1;
|
|
cubeVerts[i] = 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++) // 2 * 3 triangles.
|
|
{
|
|
args->idxSet->idxBuff[face][args->idxSet->idxCount[face]] = baseVtxCount + vtxToAddMap[ SvoViewerNames::cubeFaceIndices[face][i] ];
|
|
args->idxSet->idxCount[face]++;
|
|
}
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
args->idxSet->bounds[face].AddToSet( cubeVerts[SvoViewerNames::cubeFaceVtxs[face][i]] );
|
|
}
|
|
args->idxSet->elemCount[face] += 2;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SvoViewer::InitializeVoxelOpt2RenderSystem()
|
|
{
|
|
quint64 startInit = usecTimestampNow();
|
|
if (_voxelOptRenderInitialized) return;
|
|
_numSegments = 0;
|
|
_totalPossibleElems = 0;
|
|
memset(_numChildNodeLeaves, 0, sizeof(_numChildNodeLeaves));
|
|
memset(_segmentNodeReferences, 0, sizeof(_segmentNodeReferences));
|
|
|
|
// 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]];
|
|
memset(&_segmentIdxBuffers[i], 0, sizeof(VoxelDimIdxSet)); // Don't do it this way if we ever use a vtable for AABoundingVolumes!
|
|
for (int k = 0; k < NUM_CUBE_FACES; k++)
|
|
{
|
|
_segmentIdxBuffers[i].idxBuff[k] = new GLuint[2 * 3 * _numChildNodeLeaves[i]];
|
|
assert(_segmentIdxBuffers[i].idxBuff[k] != NULL);
|
|
}
|
|
|
|
VoxelOpt2RenderAssembleData args;
|
|
args.vtxBuffer = _readVertexStructs;
|
|
args.vtxCount = 0;
|
|
args.faceCenterCount = visFaceData.count;
|
|
args.faceCenterList = visFaceData.ptList;
|
|
args.discardedCount = 0;
|
|
args.idxSet = &_segmentIdxBuffers[i];
|
|
_systemTree.recurseNodeWithOperation(_segmentNodeReferences[i], &VoxelOpt2RenderAssemblePerVoxel, &args, 0);
|
|
|
|
SetupGlVBO(&_vboOVerticesIds[i], args.vtxCount * sizeof(Vertex), GL_ARRAY_BUFFER, GL_STATIC_DRAW, _readVertexStructs);
|
|
unsigned int idxCount = 0;
|
|
for (int k = 0; k < NUM_CUBE_FACES; k++)
|
|
{
|
|
SetupGlVBO(&_segmentIdxBuffers[i].idxIds[k], _segmentIdxBuffers[i].idxCount[k] * sizeof(GLuint),
|
|
GL_ARRAY_BUFFER, GL_STATIC_DRAW, _segmentIdxBuffers[i].idxBuff[k]);
|
|
idxCount += _segmentIdxBuffers[i].idxCount[k];
|
|
_segmentIdxBuffers[i].bounds[k].setIsSingleDirection(true, SvoViewerNames::faceNormals[k]);
|
|
}
|
|
|
|
qDebug("Partition %d, vertices %d, indices %d, discarded %d\n", i, args.vtxCount, idxCount, args.discardedCount);
|
|
|
|
delete [] _readVertexStructs;
|
|
//delete [] _readIndicesArray;
|
|
delete [] faceCenters;
|
|
for (int k = 0; k < NUM_CUBE_FACES; k++) if (_segmentIdxBuffers[i].idxBuff[k] != NULL) delete [] _segmentIdxBuffers[i].idxBuff[k];
|
|
}
|
|
|
|
_voxelOptRenderInitialized = true;
|
|
|
|
UpdateOpt2BVFaceVisibility();
|
|
|
|
quint64 endInit = usecTimestampNow();
|
|
quint64 elapsed = endInit - startInit;
|
|
qDebug() << "init elapsed:" << ((float)elapsed / (float)1000000.0f) << "seconds";
|
|
|
|
}
|
|
|
|
void SvoViewer::RenderTreeSystemAsOpt2Voxels()
|
|
{
|
|
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 )
|
|
{
|
|
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));
|
|
|
|
for (int j = 0; j < NUM_CUBE_FACES; j++)
|
|
{
|
|
// Add aggressive LOD check here.
|
|
if (_segmentIdxBuffers[i].visibleFace[j] || _useBoundingVolumes != true)
|
|
{
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _segmentIdxBuffers[i].idxIds[j]);//_vboOIndicesIds[i]);
|
|
glDrawElements(GL_TRIANGLES, _segmentIdxBuffers[i].elemCount[j]*3, GL_UNSIGNED_INT, NULL);
|
|
_numElemsDrawn += _segmentIdxBuffers[i].elemCount[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
glDisableVertexAttribArray(ATTRIB_POSITION);
|
|
glDisableVertexAttribArray(ATTRIB_COLOR);
|
|
}
|
|
|
|
// special rules for single direction bv sets. Basically, we intersect a lookat ray from the camera with two opposite faces and discard
|
|
// the entire set of the face that is further away as it must be back facing.
|
|
void SvoViewer::UpdateOpt2BVFaceVisibility()
|
|
{
|
|
if (_currentShaderModel != RENDER_OPT_CULLED_POLYS || _voxelOptRenderInitialized != true ) return;
|
|
|
|
//float faceParamVals[NUM_CUBE_FACES];
|
|
glm::vec3 pos = _myCamera.getPosition();
|
|
|
|
for (int i = 0; i < _numSegments; i++)
|
|
{
|
|
VoxelDimIdxSet* setPtr = &_segmentIdxBuffers[i];
|
|
// Fast cull check.
|
|
setPtr->visibleFace[0] = (_segmentIdxBuffers[i].bounds[0].within(pos.y, 1) >= 0) ? true : false;
|
|
setPtr->visibleFace[1] = (_segmentIdxBuffers[i].bounds[1].within(pos.y, 1) <= 0) ? true : false;
|
|
setPtr->visibleFace[2] = (_segmentIdxBuffers[i].bounds[2].within(pos.x, 0) >= 0) ? true : false;
|
|
setPtr->visibleFace[3] = (_segmentIdxBuffers[i].bounds[3].within(pos.x, 0) <= 0) ? true : false;
|
|
setPtr->visibleFace[4] = (_segmentIdxBuffers[i].bounds[4].within(pos.z, 2) <= 0) ? true : false;
|
|
setPtr->visibleFace[5] = (_segmentIdxBuffers[i].bounds[5].within(pos.z, 2) >= 0) ? true : false;
|
|
|
|
// Make sure its actually on the screen.
|
|
/*for (int j = 0; j < NUM_CUBE_FACES; j++)
|
|
{
|
|
if (setPtr->visibleFace[j])
|
|
{
|
|
if (visibleAngleSubtended(&_segmentIdxBuffers[i].bounds[j], &_myCamera, &_viewFrustum) <= 0)
|
|
setPtr->visibleFace[j] = false;
|
|
}
|
|
}*/
|
|
}
|
|
/*
|
|
for (int j = 0; j < NUM_CUBE_FACES; j++)
|
|
{
|
|
setPtr->visibleFace[i] = true;
|
|
AABoundingVolume* volume = &_segmentIdxBuffers[i].bounds[j];
|
|
glm::vec3 randomPlaneVtx = volume->getCorner((BoxVertex)SvoViewerNames::cubeFaceIndices[j][0]);
|
|
glm::vec3 raydir = randomPlaneVtx - pos;
|
|
rayder /= glm::length(raydir);
|
|
if (glm::dot(target, raydir) < 1) raydir *= -1;
|
|
if (!parameterizedRayPlaneIntersection(pos, raydir, randomPlaneVtx, SvoViewerNames::faceNormals[j], &faceParamVals[j]))
|
|
faceParamVals[j] = -1;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void SvoViewer::StopUsingVoxelOpt2RenderSystem()
|
|
{
|
|
glDisableVertexAttribArray(ATTRIB_POSITION);
|
|
glDisableVertexAttribArray(ATTRIB_COLOR);
|
|
glDisable(GL_LIGHTING);
|
|
}
|