From 030f5328f519633d043ddc49edb5ea542002f9b5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 22 Apr 2013 14:46:25 -0700 Subject: [PATCH] First cut at View Frustum Culling between client and server --- libraries/voxels/src/VoxelTree.cpp | 123 ++++++++++++++++++++++++----- libraries/voxels/src/VoxelTree.h | 3 + voxel-server/src/main.cpp | 40 ++++++++-- 3 files changed, 140 insertions(+), 26 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index bd197b53cb..e7923e5976 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -17,6 +17,7 @@ #include "PacketHeaders.h" #include "OctalCode.h" #include "VoxelTree.h" +#include "ViewFrustum.h" #include // to load voxels from file using voxels_lib::printLog; @@ -268,6 +269,8 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, MarkerNode *currentMarkerNode, float * agentPosition, float thisNodePosition[3], + const ViewFrustum& viewFrustum, + bool viewFrustumCulling, unsigned char * stopOctalCode) { static unsigned char *initialBitstreamPos = bitstreamBuffer; @@ -294,22 +297,38 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, unsigned char * childMaskPointer = NULL; float halfUnitForVoxel = powf(0.5, *currentVoxelNode->octalCode) * (0.5 * TREE_SCALE); - - // XXXBHG - Note: It appears as if the X and Z coordinates of Head or Agent are flip-flopped relative to the - // coords of the voxel space. This flip flop causes LOD behavior to be extremely odd. This is my temporary hack - // to fix this behavior. To disable this swap, set swapXandZ to false. - // XXXBHG - 2013/04/11 - adding a note to my branch, I think this code is now broken. - bool swapXandZ=false; - float agentX = swapXandZ ? agentPosition[2] : agentPosition[0]; - float agentZ = swapXandZ ? agentPosition[0] : agentPosition[2]; - - float distanceToVoxelCenter = sqrtf(powf(agentX - thisNodePosition[0] - halfUnitForVoxel, 2) + + float distanceToVoxelCenter = sqrtf(powf(agentPosition[0] - thisNodePosition[0] - halfUnitForVoxel, 2) + powf(agentPosition[1] - thisNodePosition[1] - halfUnitForVoxel, 2) + - powf(agentZ - thisNodePosition[2] - halfUnitForVoxel, 2)); + powf(agentPosition[2] - thisNodePosition[2] - halfUnitForVoxel, 2)); + + // If the voxel is outside of the view frustum, then don't bother sending or recursing + bool voxelInView = true; + + /**** not yet working properly at this level! ************************************************************************** + if (viewFrustumCulling) { + float fullUnitForVoxel = halfUnitForVoxel*2.0f; + AABox voxelBox; + voxelBox.setBox(glm::vec3(thisNodePosition[0],thisNodePosition[1],thisNodePosition[2]), + fullUnitForVoxel,fullUnitForVoxel,fullUnitForVoxel); + + //printf("VoxelTree::loadBitstreamBuffer() voxelBox.corner=(%f,%f,%f) x=%f \n", + // voxelBox.getCorner().x,voxelBox.getCorner().y,voxelBox.getCorner().z, voxelBox.getSize().x); + + voxelInView = (ViewFrustum::OUTSIDE != viewFrustum.pointInFrustum(voxelBox.getCorner())); + } else { + voxelInView = true; + } + **********************************************************************************************************************/ // if the distance to this voxel's center is less than the threshold // distance for its children, we should send the children - if (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentVoxelNode->octalCode + 1)) { + bool voxelIsClose = (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentVoxelNode->octalCode + 1)); + bool sendVoxel = voxelIsClose && voxelInView; + + //printf("VoxelTree::loadBitstreamBuffer() sendVoxel=%d, voxelIsClose=%d, voxelInView=%d, viewFrustumCulling=%d\n", + // sendVoxel, voxelIsClose, voxelInView, viewFrustumCulling); + + if (sendVoxel) { // write this voxel's data if we're below or at // or at the same level as the stopOctalCode @@ -341,16 +360,78 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, for (int i = 0; i < 8; i++) { - // check if the child exists and is not transparent - if (currentVoxelNode->children[i] != NULL - && currentVoxelNode->children[i]->isColored()) { + // Rules for including a child: + // 1) child must exists + if ((currentVoxelNode->children[i] != NULL)) { + // 2) child must have a color... + if (currentVoxelNode->children[i]->isColored()) { - // copy in the childs color to bitstreamBuffer - memcpy(colorPointer, currentVoxelNode->children[i]->getTrueColor(), 3); - colorPointer += 3; + unsigned char* childOctalCode = currentVoxelNode->children[i]->octalCode; + + float childPosition[3]; + copyFirstVertexForCode(childOctalCode,(float*)&childPosition); + childPosition[0] *= TREE_SCALE; // scale it up + childPosition[1] *= TREE_SCALE; // scale it up + childPosition[2] *= TREE_SCALE; // scale it up - // set the colorMask by bitshifting the value of childExists - *bitstreamBuffer += (1 << (7 - i)); + float halfChildVoxel = powf(0.5, *childOctalCode) * (0.5 * TREE_SCALE); + float distanceToChildCenter = sqrtf(powf(agentPosition[0] - childPosition[0] - halfChildVoxel, 2) + + powf(agentPosition[1] - childPosition[1] - halfChildVoxel, 2) + + powf(agentPosition[2] - childPosition[2] - halfChildVoxel, 2)); + + float fullChildVoxel = halfChildVoxel*2.0f; + AABox childBox; + childBox.setBox(glm::vec3(childPosition[0],childPosition[1],childPosition[2]), + fullChildVoxel,fullChildVoxel,fullChildVoxel); + + //printf("VoxelTree::loadBitstreamBuffer() childBox.corner=(%f,%f,%f) x=%f \n", + // childBox.getCorner().x,childBox.getCorner().y,childBox.getCorner().z, childBox.getSize().x); + + // XXXBHG - not sure we want to do this "distance/LOD culling" at this level. + //bool childIsClose = (distanceToChildCenter < boundaryDistanceForRenderLevel(*childOctalCode + 1)); + + bool childIsClose = true; // for now, assume we're close enough + bool childInView = !viewFrustumCulling || + (ViewFrustum::OUTSIDE != viewFrustum.pointInFrustum(childBox.getCorner())); + + /// XXXBHG - debug code, switch this to true, and we'll send everything but include false coloring + // on voxels based on whether or not they match these rules. + bool falseColorInsteadOfCulling = false; + + // removed childIsClose - until we determine if we want to include that + bool sendChild = (childInView) || falseColorInsteadOfCulling; + + //printf("VoxelTree::loadBitstreamBuffer() childIsClose=%d, childInView=%d\n", + // childIsClose, childInView); + + // if we sendAnyway, we'll do false coloring of the voxels based on childIsClose && childInView + if (sendChild) { + + // copy in the childs color to bitstreamBuffer + if (childIsClose && childInView) { + // true color + memcpy(colorPointer, currentVoxelNode->children[i]->getTrueColor(), 3); + } else { + unsigned char red[3] = {255,0,0}; + unsigned char green[3] = {0,255,0}; + unsigned char blue[3] = {0,0,255}; + if (!childIsClose && !childInView) { + // If both too far, and not in view, color them red + memcpy(colorPointer, red, 3); + } else if (!childIsClose) { + // If too far, but in view, color them blue + memcpy(colorPointer, blue, 3); + } else { + // If close, but out of view, color them green + memcpy(colorPointer, green, 3); + } + } + colorPointer += 3; + + // set the colorMask by bitshifting the value of childExists + *bitstreamBuffer += (1 << (7 - i)); + } + } } } @@ -414,6 +495,8 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, currentMarkerNode->children[i], agentPosition, childNodePosition, + viewFrustum, + viewFrustumCulling, stopOctalCode); if (bitstreamBuffer - arrBufferBeforeChild > 0) { diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 62ff4e4815..77c00b2e63 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -11,6 +11,7 @@ #include "SimpleMovingAverage.h" +#include "ViewFrustum.h" #include "VoxelNode.h" #include "MarkerNode.h" @@ -52,6 +53,8 @@ public: MarkerNode *currentMarkerNode, float * agentPosition, float thisNodePosition[3], + const ViewFrustum& viewFrustum, + bool viewFrustumCulling, unsigned char * octalCode = NULL); void loadVoxelsFile(const char* fileName, bool wantColorRandomizer); diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 8f6bab7dc7..3e411f2d16 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -48,6 +48,8 @@ const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4; VoxelTree randomTree; bool wantColorRandomizer = false; +bool debugViewFrustum = false; +bool viewFrustumCulling = false; // for now void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) { float r = random ? randFloatInRange(0.05,0.1) : 0.25; @@ -165,6 +167,24 @@ void *distributeVoxelsToListeners(void *args) { Agent *thisAgent = (Agent *)&agentList->getAgents()[i]; VoxelAgentData *agentData = (VoxelAgentData *)(thisAgent->getLinkedData()); + ViewFrustum viewFrustum; + // get position and orientation details from the camera + viewFrustum.setPosition(agentData->getCameraPosition()); + viewFrustum.setOrientation(agentData->getCameraDirection(),agentData->getCameraUp(),agentData->getCameraRight()); + + // Also make sure it's got the correct lens details from the camera + viewFrustum.setFieldOfView(agentData->getCameraFov()); + viewFrustum.setAspectRatio(agentData->getCameraAspectRatio()); + viewFrustum.setNearClip(agentData->getCameraNearClip()); + viewFrustum.setFarClip(agentData->getCameraFarClip()); + + viewFrustum.calculate(); + + // debug for fun!! + if (::debugViewFrustum) { + viewFrustum.dump(); + } + // lock this agent's delete mutex so that the delete thread doesn't // kill the agent while we are working with it pthread_mutex_lock(thisAgent->deleteMutex); @@ -181,6 +201,8 @@ void *distributeVoxelsToListeners(void *args) { agentData->rootMarkerNode, agentData->position, treeRoot, + viewFrustum, + ::viewFrustumCulling, stopOctal); agentList->getAgentSocket().send(thisAgent->getActiveSocket(), voxelPacket, voxelPacketEnd - voxelPacket); @@ -249,16 +271,23 @@ int main(int argc, const char * argv[]) agentList->startDomainServerCheckInThread(); srand((unsigned)time(0)); + + const char* DEBUG_VIEW_FRUSTUM="--DebugViewFrustum"; + ::debugViewFrustum = cmdOptionExists(argc, argv, DEBUG_VIEW_FRUSTUM); + printf("debugViewFrustum=%s\n",(::debugViewFrustum?"yes":"no")); + + const char* VIEW_FRUSTUM_CULLING="--ViewFrustumCulling"; + ::viewFrustumCulling = cmdOptionExists(argc, argv, VIEW_FRUSTUM_CULLING); + printf("viewFrustumCulling=%s\n",(::viewFrustumCulling?"yes":"no")); + const char* WANT_COLOR_RANDOMIZER="--WantColorRandomizer"; + ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); + printf("wantColorRandomizer=%s\n",(::wantColorRandomizer?"yes":"no")); + // Check to see if the user passed in a command line option for loading a local // Voxel File. If so, load it now. - const char* WANT_COLOR_RANDOMIZER="--WantColorRandomizer"; const char* INPUT_FILE="-i"; - ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); - - printf("wantColorRandomizer=%s\n",(wantColorRandomizer?"yes":"no")); const char* voxelsFilename = getCmdOption(argc, argv, INPUT_FILE); - if (voxelsFilename) { randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer); } @@ -269,7 +298,6 @@ int main(int argc, const char * argv[]) // octal codes to the tree nodes that it is creating randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, randomTree.rootNode); } - const char* ADD_SPHERE="--AddSphere"; const char* ADD_RANDOM_SPHERE="--AddRandomSphere";