From 61f23a2d0f422c356aa04619e5bfbbf7b60c312c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 14:06:20 -0700 Subject: [PATCH 01/10] add a new walk sound --- interface/resources/audio/walking4.raw | Bin 0 -> 41976 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/audio/walking4.raw diff --git a/interface/resources/audio/walking4.raw b/interface/resources/audio/walking4.raw new file mode 100644 index 0000000000000000000000000000000000000000..5d48ebd0ec781ad08639b23d791723df9c351b81 GIT binary patch literal 41976 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 T3mQMseS}-y^aL||BQ^YgkRk0T literal 0 HcmV?d00001 From c40800db8b2860f1c80397de3c2b532cb79ece58 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 15:32:36 -0700 Subject: [PATCH 02/10] send back a 10 level voxel tree from server --- shared/src/VoxelTree.cpp | 4 ++-- voxel/src/main.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index 4119bd52a0..643a7a6f8b 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -220,7 +220,7 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, // copy the childMask to the current position of the bitstreamBuffer // and push the buffer pointer forwards - *(bitstreamBuffer++) = *currentVoxelNode->octalCode < deepestLevel + *(bitstreamBuffer++) = *currentVoxelNode->octalCode < deepestLevel - 1 ? currentVoxelNode->childMask : 0; } else { @@ -231,7 +231,7 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, unsigned char * childStopOctalCode = NULL; - if (*currentVoxelNode->octalCode < deepestLevel) { + if (*currentVoxelNode->octalCode < deepestLevel - 1) { for (int i = firstIndexToCheck; i < 8; i ++) { // ask the child to load this bitstream buffer diff --git a/voxel/src/main.cpp b/voxel/src/main.cpp index 07ce2a2494..6fd757eb63 100644 --- a/voxel/src/main.cpp +++ b/voxel/src/main.cpp @@ -43,7 +43,7 @@ char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup const int DOMAINSERVER_PORT = 40102; -const int MAX_VOXEL_TREE_DEPTH_LEVELS = 6; +const int MAX_VOXEL_TREE_DEPTH_LEVELS = 10; AgentList agentList(VOXEL_LISTEN_PORT); in_addr_t localAddress; @@ -209,7 +209,7 @@ int main(int argc, const char * argv[]) agentList.updateAgentWithData(&agentPublicAddress, (void *)packetData, receivedBytes); VoxelAgentData *agentData = (VoxelAgentData *) agentList.getAgents()[agentList.indexOfMatchingAgent(&agentPublicAddress)].getLinkedData(); - int newLevel = 6; + int newLevel = 10; if (newLevel > agentData->lastSentLevel) { // the agent has already received a deeper level than this from us // do nothing From c08b7acac5cf2516de3d531f1bc94eb9b16f8a26 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 16:20:27 -0700 Subject: [PATCH 03/10] print some metrics after voxel send to client --- voxel/src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/voxel/src/main.cpp b/voxel/src/main.cpp index 6fd757eb63..ab1e2aacdf 100644 --- a/voxel/src/main.cpp +++ b/voxel/src/main.cpp @@ -217,6 +217,7 @@ int main(int argc, const char * argv[]) stopOctal = randomTree.rootNode->octalCode; packetCount = 0; totalBytesSent = 0; + randomTree.leavesWrittenToBitstream = 0; while (stopOctal != NULL) { voxelPacketEnd = voxelPacket; @@ -233,6 +234,11 @@ int main(int argc, const char * argv[]) totalBytesSent += voxelPacketEnd - voxelPacket; } + printf("%d packets sent to client totalling %d bytes\n", packetCount, totalBytesSent); + printf("%d leaves were sent - %f bpv\n", + randomTree.leavesWrittenToBitstream, + (float)totalBytesSent / randomTree.leavesWrittenToBitstream); + agentData->lastSentLevel = newLevel; } } From c51b4e331163f153dfd59bf293436ccaeb1d17b2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 16:21:45 -0700 Subject: [PATCH 04/10] add leavesWrittenToBitstream member variable to VoxelTree for debug --- shared/src/VoxelTree.cpp | 4 ++++ shared/src/VoxelTree.h | 1 + 2 files changed, 5 insertions(+) diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index 643a7a6f8b..dff2787bf8 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -231,6 +231,10 @@ unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, unsigned char * childStopOctalCode = NULL; + if (currentVoxelNode->childMask == 0) { + leavesWrittenToBitstream++; + } + if (*currentVoxelNode->octalCode < deepestLevel - 1) { for (int i = firstIndexToCheck; i < 8; i ++) { diff --git a/shared/src/VoxelTree.h b/shared/src/VoxelTree.h index a2215f5dd1..f75d99d27b 100644 --- a/shared/src/VoxelTree.h +++ b/shared/src/VoxelTree.h @@ -23,6 +23,7 @@ public: ~VoxelTree(); VoxelNode *rootNode; + int leavesWrittenToBitstream; int levelForViewerPosition(float * position); void readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes); From c641f1269399c742f24df8cf847d741905a17f16 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 17:59:54 -0700 Subject: [PATCH 05/10] some refactoring in for readCodeColorBufferToTree --- shared/src/OctalCode.cpp | 2 +- shared/src/OctalCode.h | 2 +- shared/src/VoxelTree.cpp | 9 +++------ shared/src/VoxelTree.h | 1 + 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/shared/src/OctalCode.cpp b/shared/src/OctalCode.cpp index 3b22879b75..2a687f1cd4 100644 --- a/shared/src/OctalCode.cpp +++ b/shared/src/OctalCode.cpp @@ -43,7 +43,7 @@ int bytesRequiredForCodeLength(unsigned char threeBitCodes) { } } -char branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode) { +int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode) { int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode); int branchStartBit = parentSections * 3; diff --git a/shared/src/OctalCode.h b/shared/src/OctalCode.h index a292116a75..a20cc8dfae 100644 --- a/shared/src/OctalCode.h +++ b/shared/src/OctalCode.h @@ -14,7 +14,7 @@ void printOctalCode(unsigned char * octalCode); int bytesRequiredForCodeLength(unsigned char threeBitCodes); bool isDirectParentOfChild(unsigned char *parentOctalCode, unsigned char * childOctalCode); -char branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode); +int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode); unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber); float * firstVertexForCode(unsigned char * octalCode); diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index dff2787bf8..941e6af3dc 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -12,8 +12,6 @@ #include "OctalCode.h" #include "VoxelTree.h" -const int MAX_TREE_SLICE_BYTES = 26; - VoxelTree::VoxelTree() { rootNode = new VoxelNode(); rootNode->octalCode = new unsigned char[1]; @@ -46,9 +44,7 @@ int VoxelTree::levelForViewerPosition(float *position) { VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode) { // find the appropriate branch index based on this ancestorNode - if (*needleCode == 0) { - return ancestorNode; - } else { + if (*needleCode > 0) { int branchForNeedle = branchIndexWithDescendant(ancestorNode->octalCode, needleCode); VoxelNode *childNode = ancestorNode->children[branchForNeedle]; @@ -156,7 +152,8 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { // create the node if it does not exist if (*lastCreatedNode->octalCode != *codeColorBuffer) { - lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer); + VoxelNode *parentNode = createMissingNode(lastCreatedNode, codeColorBuffer); + lastCreatedNode = parentNode->children[branchIndexWithDescendant(parentNode->octalCode, codeColorBuffer)]; } // give this node its color diff --git a/shared/src/VoxelTree.h b/shared/src/VoxelTree.h index f75d99d27b..f5b8aeeced 100644 --- a/shared/src/VoxelTree.h +++ b/shared/src/VoxelTree.h @@ -13,6 +13,7 @@ #include "VoxelNode.h" const int MAX_VOXEL_PACKET_SIZE = 1492; +const int MAX_TREE_SLICE_BYTES = 26; class VoxelTree { VoxelNode * nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode); From f435a096735ff498b8af22f8047dd4c97d0c3edb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 18:19:37 -0700 Subject: [PATCH 06/10] add method to repair child masks after adding randomly placed voxels --- shared/src/VoxelTree.cpp | 25 +++++++++++++++++++++++++ shared/src/VoxelTree.h | 1 + 2 files changed, 26 insertions(+) diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index 941e6af3dc..005ee33640 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -159,6 +159,31 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { // give this node its color memcpy(lastCreatedNode->color, codeColorBuffer + octalCodeBytes, 3); lastCreatedNode->color[3] = 1; + + repairChildMasks(rootNode); +} + +int VoxelTree::repairChildMasks(VoxelNode *currentNode) { + + currentNode->childMask = 0; + int grandChildren = 0; + int thisNodeGrandChildren = 0; + + for (int i = 0; i < 8; i++) { + + if (currentNode->children[i] != NULL) { + thisNodeGrandChildren = repairChildMasks(currentNode->children[i]); + + if (thisNodeGrandChildren > 0) { + currentNode->childMask += (1 << (7 - i)); + } + + thisNodeGrandChildren += grandChildren; + } + } + + return grandChildren; + } unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, diff --git a/shared/src/VoxelTree.h b/shared/src/VoxelTree.h index f5b8aeeced..9c5b75df35 100644 --- a/shared/src/VoxelTree.h +++ b/shared/src/VoxelTree.h @@ -29,6 +29,7 @@ public: int levelForViewerPosition(float * position); void readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes); void readCodeColorBufferToTree(unsigned char *codeColorBuffer); + int repairChildMasks(VoxelNode *currentNode); unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer, unsigned char * octalCode, VoxelNode *currentVoxelNode, From c9e16994f4b6089be8c7653b8a109902030b26f3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 Mar 2013 18:26:05 -0700 Subject: [PATCH 07/10] fix printTreeForDebugging instead of addition of repairChildMasks --- shared/src/VoxelTree.cpp | 27 +-------------------------- shared/src/VoxelTree.h | 1 - 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index 005ee33640..1c5f18b666 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -159,31 +159,6 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { // give this node its color memcpy(lastCreatedNode->color, codeColorBuffer + octalCodeBytes, 3); lastCreatedNode->color[3] = 1; - - repairChildMasks(rootNode); -} - -int VoxelTree::repairChildMasks(VoxelNode *currentNode) { - - currentNode->childMask = 0; - int grandChildren = 0; - int thisNodeGrandChildren = 0; - - for (int i = 0; i < 8; i++) { - - if (currentNode->children[i] != NULL) { - thisNodeGrandChildren = repairChildMasks(currentNode->children[i]); - - if (thisNodeGrandChildren > 0) { - currentNode->childMask += (1 << (7 - i)); - } - - thisNodeGrandChildren += grandChildren; - } - } - - return grandChildren; - } unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, @@ -311,7 +286,7 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { // ask children to recursively output their trees // if they aren't a leaf for (int k = 0; k < 8; k++) { - if (startNode->children[k] != NULL && oneAtBit(startNode->childMask, k)) { + if (startNode->children[k] != NULL) { printTreeForDebugging(startNode->children[k]); } } diff --git a/shared/src/VoxelTree.h b/shared/src/VoxelTree.h index 9c5b75df35..f5b8aeeced 100644 --- a/shared/src/VoxelTree.h +++ b/shared/src/VoxelTree.h @@ -29,7 +29,6 @@ public: int levelForViewerPosition(float * position); void readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes); void readCodeColorBufferToTree(unsigned char *codeColorBuffer); - int repairChildMasks(VoxelNode *currentNode); unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer, unsigned char * octalCode, VoxelNode *currentVoxelNode, From 8ecc0d53adba5e0c0c9035b3607b790ec723b336 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 23 Mar 2013 21:59:27 -0700 Subject: [PATCH 08/10] Added a utility to VoxelSystem for loading voxels from a local file Also added a couple shared utility functions for reading command line options (since that's how you can load a local file at this point). --- interface/src/VoxelSystem.cpp | 56 +++++++++++++++++++++++++++++++++-- interface/src/VoxelSystem.h | 2 ++ interface/src/main.cpp | 10 ++++++- shared/src/SharedUtil.cpp | 32 +++++++++++++++++++- shared/src/SharedUtil.h | 3 ++ 5 files changed, 99 insertions(+), 4 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index ebb29a8e5f..4aaee09ed7 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -8,6 +8,8 @@ #include #include +#include // to load voxels from file +#include // to load voxels from file #include #include #include @@ -46,6 +48,56 @@ VoxelSystem::~VoxelSystem() { delete tree; } +////////////////////////////////////////////////////////////////////////////////////////// +// Method: VoxelSystem::loadVoxelsFile() +// Description: Loads HiFidelity encoded Voxels from a binary file. The current file +// format is a stream of single voxels with NO color data. Currently +// colors are set randomly +// Complaints: Brad :) +// To Do: Need to add color data to the file. +void VoxelSystem::loadVoxelsFile(char* fileName) { + + std::ifstream file(fileName, std::ios::in|std::ios::binary); + + char octets; + unsigned int lengthInBytes; + + int totalBytesRead = 0; + if(file.is_open()) + { + while (!file.eof()) { + file.get(octets); + totalBytesRead++; + lengthInBytes = (octets*3/8)+1; + unsigned char * voxelData = new unsigned char[lengthInBytes+1+3]; + voxelData[0]=octets; + char byte; + + for (size_t i = 0; i < lengthInBytes; i++) { + file.get(byte); + totalBytesRead++; + voxelData[i+1] = byte; + } + // random color data + voxelData[lengthInBytes+1] = randomColorValue(65); + voxelData[lengthInBytes+2] = randomColorValue(65); + voxelData[lengthInBytes+3] = randomColorValue(65); + + tree->readCodeColorBufferToTree(voxelData); + delete voxelData; + } + file.close(); + } + + // reset the verticesEndPointer so we're writing to the beginning of the array + verticesEndPointer = verticesArray; + // call recursive function to populate in memory arrays + // it will return the number of voxels added + voxelsRendered = treeToArrays(tree->rootNode); + // set the boolean if there are any voxels to be rendered so we re-fill the VBOs + voxelsToRender = (voxelsRendered > 0); +} + void VoxelSystem::parseData(void *data, int size) { // output the bits received from the voxel server unsigned char *voxelData = (unsigned char *) data + 1; @@ -68,7 +120,7 @@ void VoxelSystem::parseData(void *data, int size) { int VoxelSystem::treeToArrays(VoxelNode *currentNode) { int voxelsAdded = 0; - + for (int i = 0; i < 8; i++) { // check if there is a child here if (currentNode->children[i] != NULL) { @@ -95,7 +147,7 @@ int VoxelSystem::treeToArrays(VoxelNode *currentNode) { delete [] startVertex; } - + return voxelsAdded; } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index e13ca27b3e..adb73808de 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -33,6 +33,8 @@ public: void render(); void setVoxelsRendered(int v) {voxelsRendered = v;}; int getVoxelsRendered() {return voxelsRendered;}; + void loadVoxelsFile(char* fileName); + private: int voxelsRendered; VoxelTree *tree; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 5759aa3c8a..e933169e1d 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -53,6 +53,7 @@ #include "Oscilloscope.h" #include "UDPSocket.h" #include "SerialInterface.h" +#include using namespace std; @@ -324,7 +325,6 @@ void initDisplay(void) void init(void) { voxels.init(); - myHead.setRenderYaw(start_yaw); head_mouse_x = WIDTH/2; @@ -969,6 +969,14 @@ int main(int argc, char** argv) printf( "Initialized Display.\n" ); init(); + + // Check to see if the user passed in a command line option for loading a local + // Voxel File. If so, load it now. + char* voxelsFilename = getCmdOption(argv, argv + argc, "-i"); + if (voxelsFilename) + { + voxels.loadVoxelsFile(voxelsFilename); + } // create thread for receipt of data via UDP pthread_create(&networkReceiveThread, NULL, networkReceive, NULL); diff --git a/shared/src/SharedUtil.cpp b/shared/src/SharedUtil.cpp index 740e6f6cfb..3340241fdd 100644 --- a/shared/src/SharedUtil.cpp +++ b/shared/src/SharedUtil.cpp @@ -73,4 +73,34 @@ void switchToResourcesIfRequired() { chdir(path); #endif -} \ No newline at end of file +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Function: getCmdOption() +// Description: Handy little function to tell you if a command line flag and option was +// included while launching the application, and to get the option value +// immediately following the flag. For example if you ran: +// ./app -i filename.txt +// then you're using the "-i" flag to set the input file name. +// Usage: char * inputFilename = getCmdOption(argv, argv + argc, "-i"); +// Complaints: Brad :) +char* getCmdOption(char ** begin, char ** end, const std::string & option) +{ + char ** itr = std::find(begin, end, option); + if (itr != end && ++itr != end) + { + return *itr; + } + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Function: getCmdOption() +// Description: Handy little function to tell you if a command line option flag was +// included while launching the application. Returns bool true/false +// Usage: bool wantDump = cmdOptionExists(argv, argv+argc, "-d"); +// Complaints: Brad :) +bool cmdOptionExists(char** begin, char** end, const std::string& option) +{ + return std::find(begin, end, option) != end; +} diff --git a/shared/src/SharedUtil.h b/shared/src/SharedUtil.h index 3c53721deb..1424fa77a5 100644 --- a/shared/src/SharedUtil.h +++ b/shared/src/SharedUtil.h @@ -31,4 +31,7 @@ bool oneAtBit(unsigned char byte, int bitIndex); void switchToResourcesIfRequired(); +char* getCmdOption(char ** begin, char ** end, const std::string& option); +bool cmdOptionExists(char** begin, char** end, const std::string& option); + #endif /* defined(__hifi__SharedUtil__) */ From ef8694dd609878abb3546c405e1ff6ff8e09152b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 24 Mar 2013 10:06:43 -0700 Subject: [PATCH 09/10] 1) Added createSphere() to VoxelSystem to create a sphere 2) Added pointToVoxel() to SharedUtils, this will give you a voxel code given x,y,z,s,r,g,b 3) Added '.' keyboard interface to create a random sphere in the local voxel system --- interface/src/VoxelSystem.cpp | 83 +++++++++++++++++ interface/src/VoxelSystem.h | 2 +- interface/src/main.cpp | 46 ++++++++++ shared/src/SharedUtil.cpp | 163 ++++++++++++++++++++++++++++++++++ shared/src/SharedUtil.h | 4 + 5 files changed, 297 insertions(+), 1 deletion(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 4aaee09ed7..3b5f6a267b 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -98,6 +98,88 @@ void VoxelSystem::loadVoxelsFile(char* fileName) { voxelsToRender = (voxelsRendered > 0); } +////////////////////////////////////////////////////////////////////////////////////////// +// Method: VoxelSystem::createSphere() +// Description: Creates a sphere of voxels in the local system at a given location/radius +// To Do: Move this function someplace better? I put it here because we need a +// mechanism to tell the system to redraw it's arrays after voxels are done +// being added. This is a concept mostly only understood by VoxelSystem. +// Complaints: Brad :) +void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid) +{ + // Psuedocode for creating a sphere: + // + // for (theta from 0 to 2pi): + // for (phi from 0 to pi): + // x = xc+r*cos(theta)*sin(phi) + // y = yc+r*sin(theta)*sin(phi) + // z = zc+r*cos(phi) + + int t=0; // total points + + // We want to make sure that as we "sweep" through our angles + // we use a delta angle that's small enough to not skip any voxels + // we can calculate theta from our desired arc length + // + // lenArc = ndeg/360deg * 2pi*R + // lenArc = theta/2pi * 2pi*R + // lenArc = theta*R + // theta = lenArc/R + // theta = g/r + float angleDelta = (s/r); + + // assume solid for now + float ri = 0.0; + if (!solid) + { + ri=r; // just the outer surface + } + // If you also iterate form the interior of the sphere to the radius, makeing + // larger and larger sphere's you'd end up with a solid sphere. And lots of voxels! + for (; ri <= r; ri+=s) + { + for (float theta=0.0; theta <= 2*M_PI; theta += angleDelta) + { + for (float phi=0.0; phi <= M_PI; phi += angleDelta) + { + t++; // total voxels + float x = xc+r*cos(theta)*sin(phi); + float y = yc+r*sin(theta)*sin(phi); + float z = zc+r*cos(phi); + /* + std::cout << " r=" << r; + std::cout << " theta=" << theta; + std::cout << " phi=" << phi; + std::cout << " x=" << x; + std::cout << " y=" << y; + std::cout << " z=" << z; + std::cout << " t=" << t; + std::cout << std::endl; + */ + + // random color data + unsigned char red = randomColorValue(65); + unsigned char green = randomColorValue(65); + unsigned char blue = randomColorValue(65); + + unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); + tree->readCodeColorBufferToTree(voxelData); + delete voxelData; + + } + } + } + + // reset the verticesEndPointer so we're writing to the beginning of the array + verticesEndPointer = verticesArray; + // call recursive function to populate in memory arrays + // it will return the number of voxels added + voxelsRendered = treeToArrays(tree->rootNode); + // set the boolean if there are any voxels to be rendered so we re-fill the VBOs + voxelsToRender = (voxelsRendered > 0); +} + + void VoxelSystem::parseData(void *data, int size) { // output the bits received from the voxel server unsigned char *voxelData = (unsigned char *) data + 1; @@ -243,3 +325,4 @@ void VoxelSystem::simulate(float deltaTime) { } + diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index adb73808de..6755886225 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -34,7 +34,7 @@ public: void setVoxelsRendered(int v) {voxelsRendered = v;}; int getVoxelsRendered() {return voxelsRendered;}; void loadVoxelsFile(char* fileName); - + void createSphere(float r,float xc, float yc, float zc, float s, bool solid); private: int voxelsRendered; VoxelTree *tree; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index e933169e1d..e572942823 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -692,6 +692,45 @@ void display(void) framecount++; } +void testPointToVoxel() +{ + float y=0; + float z=0; + float s=0.1; + for (float x=0; x<=1; x+= 0.05) + { + std::cout << " x=" << x << " "; + + unsigned char red = 200; //randomColorValue(65); + unsigned char green = 200; //randomColorValue(65); + unsigned char blue = 200; //randomColorValue(65); + + unsigned char* voxelCode = pointToVoxel(x, y, z, s,red,green,blue); + printVoxelCode(voxelCode); + delete voxelCode; + std::cout << std::endl; + } +} + +void addRandomSphere() +{ + float r = randFloatInRange(0.05,0.1); + float xc = randFloatInRange(r,(1-r)); + float yc = randFloatInRange(r,(1-r)); + float zc = randFloatInRange(r,(1-r)); + float s = 0.002; // size of voxels to make up surface of sphere + bool solid = false; + + printf("random sphere\n"); + printf("radius=%f\n",r); + printf("xc=%f\n",xc); + printf("yc=%f\n",yc); + printf("zc=%f\n",zc); + + voxels.createSphere(r,xc,yc,zc,s,solid); +} + + const float KEYBOARD_YAW_RATE = 0.8; const float KEYBOARD_STRAFE_RATE = 0.03; const float KEYBOARD_FLY_RATE = 0.08; @@ -766,6 +805,13 @@ void key(unsigned char k, int x, int y) { myHead.SetNewHeadTarget((randFloat()-0.5)*20.0, (randFloat()-0.5)*20.0); } + + // press the . key to get a new random sphere of voxels added + if (k == '.') + { + addRandomSphere(); + //testPointToVoxel(); + } } // diff --git a/shared/src/SharedUtil.cpp b/shared/src/SharedUtil.cpp index 3340241fdd..3e9e75e2bb 100644 --- a/shared/src/SharedUtil.cpp +++ b/shared/src/SharedUtil.cpp @@ -28,6 +28,10 @@ float randFloat () { return (rand() % 10000)/10000.f; } +float randFloatInRange (float min,float max) { + return min + ((rand() % 10000)/10000.f * (max-min)); +} + unsigned char randomColorValue(int miniumum) { return miniumum + (rand() % (255 - miniumum)); } @@ -104,3 +108,162 @@ bool cmdOptionExists(char** begin, char** end, const std::string& option) { return std::find(begin, end, option) != end; } + +////////////////////////////////////////////////////////////////////////////////////////// +// Function: pointToVoxel() +// Description: Given a universal point with location x,y,z this will return the voxel +// voxel code corresponding to the closest voxel which encloses a cube with +// lower corners at x,y,z, having side of length S. +// The input values x,y,z range 0.0 <= v < 1.0 +// TO DO: This code is not very DRY. It should be cleaned up to be DRYer. +// IMPORTANT: The voxel is returned to you a buffer which you MUST delete when you are +// done with it. +// Usage: +// unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); +// tree->readCodeColorBufferToTree(voxelData); +// delete voxelData; +// +// Complaints: Brad :) +unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b ) { + + float xTest, yTest, zTest, sTest; + xTest = yTest = zTest = sTest = 0.5; + + // First determine the voxelSize that will properly encode a + // voxel of size S. + int voxelSizeInBits = 0; + while (sTest > s) { + sTest /= 2.0; + voxelSizeInBits+=3; + } + + unsigned int voxelSizeInBytes = (voxelSizeInBits/8)+1; + unsigned int voxelSizeInOctets = (voxelSizeInBits/3); + unsigned int voxelBufferSize = voxelSizeInBytes+1+3; // 1 for size, 3 for color + + // allocate our resulting buffer + unsigned char* voxelOut = new unsigned char[voxelBufferSize]; + + // first byte of buffer is always our size in octets + voxelOut[0]=voxelSizeInOctets; + + sTest = 0.5; // reset sTest so we can do this again. + + unsigned char byte = 0; // we will be adding coding bits here + int bitInByteNDX = 0; // keep track of where we are in byte as we go + int byteNDX = 1; // keep track of where we are in buffer of bytes as we go + int octetsDone = 0; + + // Now we actually fill out the voxel code + while (octetsDone < voxelSizeInOctets) { + + if (x > xTest) { + // + byte = (byte << 1) | true; + xTest += sTest/2.0; + } + else { + // + byte = (byte << 1) | false; + xTest -= sTest/2.0; + } + bitInByteNDX++; + // If we've reached the last bit of the byte, then we want to copy this byte + // into our buffer. And get ready to start on a new byte + if (bitInByteNDX > 7) + { + voxelOut[byteNDX]=byte; + byteNDX++; + bitInByteNDX=0; + byte=0; + } + + if (y > yTest) { + // + byte = (byte << 1) | true; + yTest += sTest/2.0; + } + else { + // + byte = (byte << 1) | false; + yTest -= sTest/2.0; + } + bitInByteNDX++; + // If we've reached the last bit of the byte, then we want to copy this byte + // into our buffer. And get ready to start on a new byte + if (bitInByteNDX > 7) + { + voxelOut[byteNDX]=byte; + byteNDX++; + bitInByteNDX=0; + byte=0; + } + + if (z > zTest) { + // + byte = (byte << 1) | true; + zTest += sTest/2.0; + } + else { + // + byte = (byte << 1) | false; + zTest -= sTest/2.0; + } + bitInByteNDX++; + // If we've reached the last bit of the byte, then we want to copy this byte + // into our buffer. And get ready to start on a new byte + if (bitInByteNDX > 7) + { + voxelOut[byteNDX]=byte; + byteNDX++; + bitInByteNDX=0; + byte=0; + } + + octetsDone++; + sTest /= 2.0; + } + + // If we've got here, and we didn't fill the last byte, we need to zero pad this + // byte before we copy it into our buffer. + if (bitInByteNDX > 0 && bitInByteNDX < 7) + { + // Pad the last byte + while (bitInByteNDX <= 7) + { + byte = (byte << 1) | false; + bitInByteNDX++; + } + + // Copy it into our output buffer + voxelOut[byteNDX]=byte; + byteNDX++; + } + // copy color data + voxelOut[byteNDX]=r; + voxelOut[byteNDX+1]=g; + voxelOut[byteNDX+2]=b; + + return voxelOut; +} + +void printVoxelCode(unsigned char* voxelCode) +{ + unsigned char octets = voxelCode[0]; + unsigned int voxelSizeInBits = octets*3; + unsigned int voxelSizeInBytes = (voxelSizeInBits/8)+1; + unsigned int voxelSizeInOctets = (voxelSizeInBits/3); + unsigned int voxelBufferSize = voxelSizeInBytes+1+3; // 1 for size, 3 for color + + printf("octets=%d\n",octets); + printf("voxelSizeInBits=%d\n",voxelSizeInBits); + printf("voxelSizeInBytes=%d\n",voxelSizeInBytes); + printf("voxelSizeInOctets=%d\n",voxelSizeInOctets); + printf("voxelBufferSize=%d\n",voxelBufferSize); + + for(int i=0;i Date: Sun, 24 Mar 2013 13:00:23 -0700 Subject: [PATCH 10/10] Changed colors of spheres to be a gradient along phi between to random colors this gives the spheres a little bit more natural look. Also increased the level of detail (smaller voxels) --- interface/src/VoxelSystem.cpp | 37 +++++++++++++++++++++++++++++++---- interface/src/main.cpp | 2 +- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 3b5f6a267b..4e88d01ddf 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -107,6 +107,34 @@ void VoxelSystem::loadVoxelsFile(char* fileName) { // Complaints: Brad :) void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid) { + // About the color of the sphere... we're going to make this sphere be a gradient + // between two RGB colors. We will do the gradient along the phi spectrum + unsigned char r1 = randomColorValue(165); + unsigned char g1 = randomColorValue(165); + unsigned char b1 = randomColorValue(165); + unsigned char r2 = randomColorValue(65); + unsigned char g2 = randomColorValue(65); + unsigned char b2 = randomColorValue(65); + + // we don't want them to match!! + if (r1==r2 && g1==g2 && b1==b2) + { + r2=r1/2; + g2=g1/2; + b2=b1/2; + } + + /** + std::cout << "creatSphere COLORS "; + std::cout << " r1=" << (int)r1; + std::cout << " g1=" << (int)g1; + std::cout << " b1=" << (int)b1; + std::cout << " r2=" << (int)r2; + std::cout << " g2=" << (int)g2; + std::cout << " b2=" << (int)b2; + std::cout << std::endl; + **/ + // Psuedocode for creating a sphere: // // for (theta from 0 to 2pi): @@ -157,10 +185,11 @@ void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bo std::cout << std::endl; */ - // random color data - unsigned char red = randomColorValue(65); - unsigned char green = randomColorValue(65); - unsigned char blue = randomColorValue(65); + // gradient color data + float gradient = (phi/M_PI); + unsigned char red = r1+((r2-r1)*gradient); + unsigned char green = g1+((g2-g1)*gradient); + unsigned char blue = b1+((b2-b1)*gradient); unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); tree->readCodeColorBufferToTree(voxelData); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index e572942823..3df7672a2f 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -718,7 +718,7 @@ void addRandomSphere() float xc = randFloatInRange(r,(1-r)); float yc = randFloatInRange(r,(1-r)); float zc = randFloatInRange(r,(1-r)); - float s = 0.002; // size of voxels to make up surface of sphere + float s = 0.001; // size of voxels to make up surface of sphere bool solid = false; printf("random sphere\n");