diff --git a/domain/src/main.cpp b/domain/src/main.cpp index 4db6bc45b9..7ef0967e1b 100644 --- a/domain/src/main.cpp +++ b/domain/src/main.cpp @@ -37,6 +37,7 @@ #include #endif + const int DOMAIN_LISTEN_PORT = 40102; unsigned char packetData[MAX_PACKET_SIZE]; @@ -60,6 +61,21 @@ unsigned char * addAgentToBroadcastPacket(unsigned char *currentPosition, Agent int main(int argc, const char * argv[]) { + // If user asks to run in "local" mode then we do NOT replace the IP + // with the EC2 IP. Otherwise, we will replace the IP like we used to + // this allows developers to run a local domain without recompiling the + // domain server + bool useLocal = cmdOptionExists(argc, argv, "--local"); + if (useLocal) { + printf("NOTE: Running in Local Mode!\n"); + } else { + printf("--------------------------------------------------\n"); + printf("NOTE: Running in EC2 Mode. \n"); + printf("If you're a developer testing a local system, you\n"); + printf("probably want to include --local on command line.\n"); + printf("--------------------------------------------------\n"); + } + setvbuf(stdout, NULL, _IOLBF, 0); ssize_t receivedBytes = 0; @@ -90,7 +106,11 @@ int main(int argc, const char * argv[]) // if it matches our local address we're on the same box // so hardcode the EC2 public address for now if (agentPublicAddress.sin_addr.s_addr == serverLocalAddress) { - agentPublicAddress.sin_addr.s_addr = 895283510; + // If we're not running "local" then we do replace the IP + // with the EC2 IP. Otherwise, we use our normal public IP + if (!useLocal) { + agentPublicAddress.sin_addr.s_addr = 895283510; // local IP in this format... + } } if (agentList.addOrUpdateAgent((sockaddr *)&agentPublicAddress, diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index df06c07cfc..8aff6decb7 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -158,9 +158,6 @@ int audioCallback (const void *inputBuffer, currentPacketPtr += sizeof(float); } - // tell the mixer not to add additional attenuation to our source - *(currentPacketPtr++) = 255; - // memcpy the corrected render yaw float correctedYaw = fmodf(data->linkedHead->getRenderYaw(), 360); @@ -177,35 +174,35 @@ int audioCallback (const void *inputBuffer, memcpy(currentPacketPtr, &correctedYaw, sizeof(float)); currentPacketPtr += sizeof(float); - if (samplesLeftForWalk == 0) { - sampleWalkPointer = walkingSoundArray; - } - - if (data->playWalkSound) { - // if this boolean is true and we aren't currently playing the walk sound - // set the number of samples left for walk - samplesLeftForWalk = walkingSoundSamples; - data->playWalkSound = false; - } - - if (samplesLeftForWalk > 0) { - // we need to play part of the walking sound - // so add it in - int affectedSamples = std::min(samplesLeftForWalk, BUFFER_LENGTH_SAMPLES); - for (int i = 0; i < affectedSamples; i++) { - inputLeft[i] += *sampleWalkPointer; - inputLeft[i] = std::max(inputLeft[i], std::numeric_limits::min()); - inputLeft[i] = std::min(inputLeft[i], std::numeric_limits::max()); - - sampleWalkPointer++; - samplesLeftForWalk--; - - if (sampleWalkPointer - walkingSoundArray > walkingSoundSamples) { - sampleWalkPointer = walkingSoundArray; - }; - } - } - +// if (samplesLeftForWalk == 0) { +// sampleWalkPointer = walkingSoundArray; +// } +// +// if (data->playWalkSound) { +// // if this boolean is true and we aren't currently playing the walk sound +// // set the number of samples left for walk +// samplesLeftForWalk = walkingSoundSamples; +// data->playWalkSound = false; +// } +// +// if (samplesLeftForWalk > 0) { +// // we need to play part of the walking sound +// // so add it in +// int affectedSamples = std::min(samplesLeftForWalk, BUFFER_LENGTH_SAMPLES); +// for (int i = 0; i < affectedSamples; i++) { +// inputLeft[i] += *sampleWalkPointer; +// inputLeft[i] = std::max(inputLeft[i], std::numeric_limits::min()); +// inputLeft[i] = std::min(inputLeft[i], std::numeric_limits::max()); +// +// sampleWalkPointer++; +// samplesLeftForWalk--; +// +// if (sampleWalkPointer - walkingSoundArray > walkingSoundSamples) { +// sampleWalkPointer = walkingSoundArray; +// }; +// } +// } +// // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet @@ -267,19 +264,19 @@ int audioCallback (const void *inputBuffer, } // check if we have more than we need to play out - int thresholdFrames = ceilf((PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) / (float)PACKET_LENGTH_SAMPLES); - int thresholdSamples = thresholdFrames * PACKET_LENGTH_SAMPLES; - - if (ringBuffer->diffLastWriteNextOutput() > thresholdSamples) { - // we need to push the next output forwards - int samplesToPush = ringBuffer->diffLastWriteNextOutput() - thresholdSamples; - - if (ringBuffer->getNextOutput() + samplesToPush > ringBuffer->getBuffer()) { - ringBuffer->setNextOutput(ringBuffer->getBuffer() + (samplesToPush - (ringBuffer->getBuffer() + RING_BUFFER_SAMPLES - ringBuffer->getNextOutput()))); - } else { - ringBuffer->setNextOutput(ringBuffer->getNextOutput() + samplesToPush); - } - } +// int thresholdFrames = ceilf((PACKET_LENGTH_SAMPLES + JITTER_BUFFER_SAMPLES) / (float)PACKET_LENGTH_SAMPLES); +// int thresholdSamples = thresholdFrames * PACKET_LENGTH_SAMPLES; +// +// if (ringBuffer->diffLastWriteNextOutput() > thresholdSamples) { +// // we need to push the next output forwards +// int samplesToPush = ringBuffer->diffLastWriteNextOutput() - thresholdSamples; +// +// if (ringBuffer->getNextOutput() + samplesToPush > ringBuffer->getBuffer()) { +// ringBuffer->setNextOutput(ringBuffer->getBuffer() + (samplesToPush - (ringBuffer->getBuffer() + RING_BUFFER_SAMPLES - ringBuffer->getNextOutput()))); +// } else { +// ringBuffer->setNextOutput(ringBuffer->getNextOutput() + samplesToPush); +// } +// } for (int s = 0; s < PACKET_LENGTH_SAMPLES_PER_CHANNEL; s++) { diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index adcec11297..188f4972bc 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -32,14 +32,12 @@ float browThickness = 0.16; const float DECAY = 0.1; -char iris_texture_file[] = "images/green_eye.png"; +char iris_texture_file[] = "resources/images/green_eye.png"; vector iris_texture; unsigned int iris_texture_width = 512; unsigned int iris_texture_height = 256; -GLUquadric *sphere = gluNewQuadric(); - Head::Head() { position = glm::vec3(0,0,0); @@ -80,6 +78,8 @@ Head::Head() browAudioLift = 0.0; noise = 0; + sphere = NULL; + hand = new Hand(glm::vec3(skinColor[0], skinColor[1], skinColor[2])); if (iris_texture.size() == 0) { @@ -135,12 +135,14 @@ Head::Head(const Head &otherHead) { browAudioLift = otherHead.browAudioLift; noise = otherHead.noise; + sphere = NULL; + Hand newHand = Hand(*otherHead.hand); hand = &newHand; } Head::~Head() { - if (sphere) { + if (sphere != NULL) { gluDeleteQuadric(sphere); } } @@ -454,7 +456,7 @@ void Head::render(int faceToFace, int isMine) glPopMatrix(); // Right Pupil - if (!sphere) { + if (sphere == NULL) { sphere = gluNewQuadric(); gluQuadricTexture(sphere, GL_TRUE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); diff --git a/interface/src/Head.h b/interface/src/Head.h index 6ab4368d82..abde89deea 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -130,6 +130,8 @@ class Head : public AgentData { int eyeContact; eyeContactTargets eyeContactTarget; + GLUquadric *sphere; + void readSensors(); float renderYaw, renderPitch; // Pitch from view frustum when this is own head. diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index f20ba78102..13bb2245f3 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -15,9 +15,10 @@ #include // to load voxels from file #include #include +#include #include "VoxelSystem.h" -const int MAX_VOXELS_PER_SYSTEM = 250000; +const int MAX_VOXELS_PER_SYSTEM = 1500000; //250000; const int VERTICES_PER_VOXEL = 8; const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; @@ -42,12 +43,20 @@ GLubyte identityIndices[] = { 0,1,2, 0,2,3, VoxelSystem::VoxelSystem() { voxelsRendered = 0; tree = new VoxelTree(); + pthread_mutex_init(&bufferWriteLock, NULL); } VoxelSystem::~VoxelSystem() { - delete[] verticesArray; - delete[] colorsArray; + delete[] readVerticesArray; + delete[] writeVerticesArray; + delete[] readColorsArray; + delete[] writeColorsArray; delete tree; + pthread_mutex_destroy(&bufferWriteLock); +} + +void VoxelSystem::setViewerHead(Head *newViewerHead) { + viewerHead = newViewerHead; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -57,65 +66,11 @@ VoxelSystem::~VoxelSystem() { // colors are set randomly // Complaints: Brad :) // To Do: Need to add color data to the file. -void VoxelSystem::loadVoxelsFile(char* fileName) { - int vCount = 0; - - std::ifstream file(fileName, std::ios::in|std::ios::binary); - - char octets; - unsigned int lengthInBytes; +void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { - int totalBytesRead = 0; - if(file.is_open()) - { - bool bail = false; - while (!file.eof() && !bail) { - file.get(octets); - totalBytesRead++; - lengthInBytes = bytesRequiredForCodeLength(octets)-1; //(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; - } - // read color data - char red,green,blue; - file.get(red); - file.get(green); - file.get(blue); - - //printf("red:%d\n",red); - //printf("green:%d\n",green); - //printf("blue:%d\n",blue); - vCount++; - //printf("vCount:%d\n",vCount); - - //randomColorValue(65); - float rf = randFloatInRange(.5,1); // add a little bit of variance to colors so we can see the voxels - voxelData[lengthInBytes+1] = red * rf; - voxelData[lengthInBytes+2] = green * rf; - voxelData[lengthInBytes+3] = blue * rf; - - //printVoxelCode(voxelData); - tree->readCodeColorBufferToTree(voxelData); - delete voxelData; - } - file.close(); - } + tree->loadVoxelsFile(fileName,wantColorRandomizer); - tree->pruneTree(tree->rootNode); - - // 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); + copyWrittenDataToReadArrays(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -125,107 +80,10 @@ void VoxelSystem::loadVoxelsFile(char* fileName) { // 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) -{ - // 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; - } +void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) { - /** - 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): - // 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; - */ - - // 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); - 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); + tree->createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer); + setupNewVoxelsForDrawing(); } @@ -233,32 +91,74 @@ void VoxelSystem::parseData(void *data, int size) { // output the bits received from the voxel server unsigned char *voxelData = (unsigned char *) data + 1; - printf("Received a packet of %d bytes from VS\n", size); - // ask the VoxelTree to read the bitstream into the tree tree->readBitstreamToTree(voxelData, size - 1); - // 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); + setupNewVoxelsForDrawing(); } -int VoxelSystem::treeToArrays(VoxelNode *currentNode) { +void VoxelSystem::setupNewVoxelsForDrawing() { + // reset the verticesEndPointer so we're writing to the beginning of the array + writeVerticesEndPointer = writeVerticesArray; + // call recursive function to populate in memory arrays + // it will return the number of voxels added + float treeRoot[3] = {0,0,0}; + voxelsRendered = treeToArrays(tree->rootNode, treeRoot); + + // copy the newly written data to the arrays designated for reading + copyWrittenDataToReadArrays(); +} + +void VoxelSystem::copyWrittenDataToReadArrays() { + // lock on the buffer write lock so we can't modify the data when the GPU is reading it + pthread_mutex_lock(&bufferWriteLock); + // store a pointer to the current end so it doesn't change during copy + GLfloat *endOfCurrentVerticesData = writeVerticesEndPointer; + // copy the vertices and colors + memcpy(readVerticesArray, writeVerticesArray, (endOfCurrentVerticesData - writeVerticesArray) * sizeof(GLfloat)); + memcpy(readColorsArray, writeColorsArray, (endOfCurrentVerticesData - writeVerticesArray) * sizeof(GLubyte)); + + // set the read vertices end pointer to the correct spot so the GPU knows how much to pull + readVerticesEndPointer = readVerticesArray + (endOfCurrentVerticesData - writeVerticesArray); + pthread_mutex_unlock(&bufferWriteLock); +} + +int VoxelSystem::treeToArrays(VoxelNode *currentNode, float nodePosition[3]) { int voxelsAdded = 0; - for (int i = 0; i < 8; i++) { - // check if there is a child here - if (currentNode->children[i] != NULL) { - voxelsAdded += treeToArrays(currentNode->children[i]); + float halfUnitForVoxel = powf(0.5, *currentNode->octalCode) * (0.5 * TREE_SCALE); + glm::vec3 viewerPosition = viewerHead->getPos(); + + float distanceToVoxelCenter = sqrtf(powf(viewerPosition[0] - nodePosition[0] - halfUnitForVoxel, 2) + + powf(viewerPosition[1] - nodePosition[1] - halfUnitForVoxel, 2) + + powf(viewerPosition[2] - nodePosition[2] - halfUnitForVoxel, 2)); + + if (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentNode->octalCode + 1)) { + for (int i = 0; i < 8; i++) { + // check if there is a child here + if (currentNode->children[i] != NULL) { + + // calculate the child's position based on the parent position + float childNodePosition[3]; + + for (int j = 0; j < 3; j++) { + childNodePosition[j] = nodePosition[j]; + + if (oneAtBit(branchIndexWithDescendant(currentNode->octalCode, + currentNode->children[i]->octalCode), + (7 - j))) { + childNodePosition[j] -= (powf(0.5, *currentNode->children[i]->octalCode) * TREE_SCALE); + } + } + + + voxelsAdded += treeToArrays(currentNode->children[i], childNodePosition); + } } } + + // if we didn't get any voxels added then we're a leaf // add our vertex and color information to the interleaved array if (voxelsAdded == 0 && currentNode->color[3] == 1) { @@ -268,10 +168,10 @@ int VoxelSystem::treeToArrays(VoxelNode *currentNode) { // populate the array with points for the 8 vertices // and RGB color for each added vertex for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) { - *verticesEndPointer = startVertex[j % 3] + (identityVertices[j] * voxelScale); - *(colorsArray + (verticesEndPointer - verticesArray)) = currentNode->color[j % 3]; + *writeVerticesEndPointer = startVertex[j % 3] + (identityVertices[j] * voxelScale); + *(writeColorsArray + (writeVerticesEndPointer - writeVerticesArray)) = currentNode->color[j % 3]; - verticesEndPointer++; + writeVerticesEndPointer++; } voxelsAdded++; @@ -289,8 +189,10 @@ VoxelSystem* VoxelSystem::clone() const { void VoxelSystem::init() { // prep the data structures for incoming voxel data - verticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; - colorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; + readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; GLuint *indicesArray = new GLuint[INDICES_PER_VOXEL * MAX_VOXELS_PER_SYSTEM]; @@ -321,7 +223,9 @@ void VoxelSystem::init() { // VBO for the indicesArray glGenBuffers(1, &vboIndicesID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndicesID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_VOXEL * sizeof(GLuint) * MAX_VOXELS_PER_SYSTEM, indicesArray, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + INDICES_PER_VOXEL * sizeof(GLuint) * MAX_VOXELS_PER_SYSTEM, + indicesArray, GL_STATIC_DRAW); // delete the indices array that is no longer needed delete[] indicesArray; @@ -331,16 +235,24 @@ void VoxelSystem::render() { glPushMatrix(); - if (voxelsToRender) { - glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID); - glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, (verticesEndPointer - verticesArray) * sizeof(GLfloat), verticesArray); - - glBindBuffer(GL_ARRAY_BUFFER, vboColorsID); - glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, (verticesEndPointer - verticesArray) * sizeof(GLubyte), colorsArray); - - voxelsToRender = false; + + if (readVerticesEndPointer != readVerticesArray) { + // try to lock on the buffer write + // just avoid pulling new data if it is currently being written + if (pthread_mutex_trylock(&bufferWriteLock) == 0) { + + glBindBuffer(GL_ARRAY_BUFFER, vboVerticesID); + glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, (readVerticesEndPointer - readVerticesArray) * sizeof(GLfloat), readVerticesArray); + + glBindBuffer(GL_ARRAY_BUFFER, vboColorsID); + glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * MAX_VOXELS_PER_SYSTEM, NULL, GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, (readVerticesEndPointer - readVerticesArray) * sizeof(GLubyte), readColorsArray); + + readVerticesEndPointer = readVerticesArray; + + pthread_mutex_unlock(&bufferWriteLock); + } } // tell OpenGL where to find vertex and color information diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 6755886225..30c4ff9d64 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -15,6 +15,7 @@ #include #include #include +#include "Head.h" #include "Util.h" #include "world.h" @@ -33,20 +34,27 @@ public: void render(); 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); + void setViewerHead(Head *newViewerHead); + void loadVoxelsFile(const char* fileName,bool wantColorRandomizer); + void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer); private: int voxelsRendered; + Head *viewerHead; VoxelTree *tree; - bool voxelsToRender; - GLfloat *verticesArray; - GLubyte *colorsArray; - GLfloat *verticesEndPointer; + GLfloat *readVerticesArray; + GLubyte *readColorsArray; + GLfloat *readVerticesEndPointer; + GLfloat *writeVerticesArray; + GLubyte *writeColorsArray; + GLfloat *writeVerticesEndPointer; GLuint vboVerticesID; GLuint vboColorsID; GLuint vboIndicesID; + pthread_mutex_t bufferWriteLock; - int treeToArrays(VoxelNode *currentNode); + int treeToArrays(VoxelNode *currentNode, float nodePosition[3]); + void setupNewVoxelsForDrawing(); + void copyWrittenDataToReadArrays(); }; #endif diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 75dd5e2850..693384d181 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -81,6 +81,8 @@ int WIDTH = 1200; int HEIGHT = 800; int fullscreen = 0; +bool wantColorRandomizer = true; // for addSphere and load file + Oscilloscope audioScope(256,200,true); #define HAND_RADIUS 0.25 // Radius of in-world 'hand' of you @@ -292,6 +294,7 @@ void initDisplay(void) void init(void) { voxels.init(); + voxels.setViewerHead(&myHead); myHead.setRenderYaw(start_yaw); head_mouse_x = WIDTH/2; @@ -476,9 +479,9 @@ void display(void) GLfloat light_position0[] = { 1.0, 1.0, 0.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); - GLfloat ambient_color[] = { 0.125, 0.305, 0.5 }; + GLfloat ambient_color[] = { 0.7, 0.7, 0.8 }; //{ 0.125, 0.305, 0.5 }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); - GLfloat diffuse_color[] = { 0.5, 0.42, 0.33 }; + GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 }; //{ 0.5, 0.42, 0.33 }; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color); @@ -627,7 +630,7 @@ void testPointToVoxel() } } -void addRandomSphere() +void addRandomSphere(bool wantColorRandomizer) { float r = randFloatInRange(0.05,0.1); float xc = randFloatInRange(r,(1-r)); @@ -642,7 +645,7 @@ void addRandomSphere() printf("yc=%f\n",yc); printf("zc=%f\n",zc); - voxels.createSphere(r,xc,yc,zc,s,solid); + voxels.createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer); } @@ -761,7 +764,7 @@ void key(unsigned char k, int x, int y) // press the . key to get a new random sphere of voxels added if (k == '.') { - addRandomSphere(); + addRandomSphere(wantColorRandomizer); //testPointToVoxel(); } } @@ -898,8 +901,20 @@ void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) { } #endif -int main(int argc, char** argv) +int main(int argc, const char * argv[]) { + const char* domainIP = getCmdOption(argc, argv, "--domain"); + if (domainIP) { + strcpy(DOMAIN_IP,domainIP); + } + + // Handle Local Domain testing with the --local command line + if (cmdOptionExists(argc, argv, "--local")) { + printf("Local Domain MODE!\n"); + int ip = getLocalAddress(); + sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + } + // the callback for our instance of AgentList is attachNewHeadToAgent agentList.linkedDataCreateCallback = &attachNewHeadToAgent; @@ -916,7 +931,7 @@ int main(int argc, char** argv) agentList.startSilentAgentRemovalThread(); agentList.startDomainServerCheckInThread(); - glutInit(&argc, argv); + glutInit(&argc, (char**)argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(WIDTH, HEIGHT); glutCreateWindow("Interface"); @@ -944,12 +959,16 @@ int main(int argc, char** argv) init(); + // Check to see if the user passed in a command line option for randomizing colors + if (cmdOptionExists(argc, argv, "--NoColorRandomizer")) { + wantColorRandomizer = false; + } + // 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(argc, argv, "-i"); - if (voxelsFilename) - { - voxels.loadVoxelsFile(voxelsFilename); + const char* voxelsFilename = getCmdOption(argc, argv, "-i"); + if (voxelsFilename) { + voxels.loadVoxelsFile(voxelsFilename,wantColorRandomizer); } // create thread for receipt of data via UDP diff --git a/mixer/src/main.cpp b/mixer/src/main.cpp index ea97e85d0e..ce0e5c16a0 100644 --- a/mixer/src/main.cpp +++ b/mixer/src/main.cpp @@ -1,4 +1,10 @@ -#ifndef _WIN32 +// +// main.cpp +// mixer +// +// Created by Stephen Birarda on 2/1/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// #include #include @@ -31,7 +37,7 @@ const unsigned short MIXER_LISTEN_PORT = 55443; const float SAMPLE_RATE = 22050.0; const short JITTER_BUFFER_MSECS = 12; -const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0f); +const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0); const int BUFFER_LENGTH_BYTES = 1024; const int BUFFER_LENGTH_SAMPLES_PER_CHANNEL = (BUFFER_LENGTH_BYTES / 2) / sizeof(int16_t); @@ -44,8 +50,8 @@ const float BUFFER_SEND_INTERVAL_USECS = (BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SA const long MAX_SAMPLE_VALUE = std::numeric_limits::max(); const long MIN_SAMPLE_VALUE = std::numeric_limits::min(); -const float DISTANCE_RATIO = 3.0f/4.2f; -const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5f; +const float DISTANCE_RATIO = 3.0/4.2; +const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5; const int PHASE_DELAY_AT_90 = 20; @@ -185,15 +191,13 @@ void *sendBuffer(void *args) // pull the earlier sample for the delayed channel int earlierSample = delaySamplePointer[s] * - distanceCoeffs[lowAgentIndex][highAgentIndex] * - otherAgentBuffer->getAttenuationRatio(); + distanceCoeffs[lowAgentIndex][highAgentIndex]; plateauAdditionOfSamples(delayedChannel[s], earlierSample * weakChannelAmplitudeRatio); } int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * - distanceCoeffs[lowAgentIndex][highAgentIndex]) * - otherAgentBuffer->getAttenuationRatio(); + distanceCoeffs[lowAgentIndex][highAgentIndex]); plateauAdditionOfSamples(goodChannel[s], currentSample); if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { @@ -304,12 +308,3 @@ int main(int argc, const char * argv[]) return 0; } - -#else - -int main(int argc, const char * argv[]) -{ - return 0; -} - -#endif _WIN32 diff --git a/shared/src/Agent.cpp b/shared/src/Agent.cpp index 8ec12c9020..2b2277b347 100644 --- a/shared/src/Agent.cpp +++ b/shared/src/Agent.cpp @@ -6,6 +6,7 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include #include "Agent.h" #include #include "UDPSocket.h" @@ -34,6 +35,8 @@ Agent::Agent(sockaddr *agentPublicSocket, sockaddr *agentLocalSocket, char agent activeSocket = NULL; linkedData = NULL; + + pthread_mutex_init(&deleteMutex, NULL); } Agent::Agent(const Agent &otherAgent) { @@ -62,6 +65,8 @@ Agent::Agent(const Agent &otherAgent) { } else { linkedData = NULL; } + + deleteMutex = otherAgent.deleteMutex; } Agent& Agent::operator=(Agent otherAgent) { @@ -69,6 +74,17 @@ Agent& Agent::operator=(Agent otherAgent) { return *this; } +void Agent::swap(Agent &first, Agent &second) { + using std::swap; + swap(first.publicSocket, second.publicSocket); + swap(first.localSocket, second.localSocket); + swap(first.activeSocket, second.activeSocket); + swap(first.type, second.type); + swap(first.linkedData, second.linkedData); + swap(first.agentId, second.agentId); + swap(first.deleteMutex, second.deleteMutex); +} + Agent::~Agent() { delete publicSocket; delete localSocket; @@ -148,16 +164,6 @@ bool Agent::operator==(const Agent& otherAgent) { return matches(otherAgent.publicSocket, otherAgent.localSocket, otherAgent.type); } -void Agent::swap(Agent &first, Agent &second) { - using std::swap; - swap(first.publicSocket, second.publicSocket); - swap(first.localSocket, second.localSocket); - swap(first.activeSocket, second.activeSocket); - swap(first.type, second.type); - swap(first.linkedData, second.linkedData); - swap(first.agentId, second.agentId); -} - bool Agent::matches(sockaddr *otherPublicSocket, sockaddr *otherLocalSocket, char otherAgentType) { // checks if two agent objects are the same agent (same type + local + public address) return type == otherAgentType diff --git a/shared/src/Agent.h b/shared/src/Agent.h index 4fc7bd9a83..1b86e95e9b 100644 --- a/shared/src/Agent.h +++ b/shared/src/Agent.h @@ -20,42 +20,45 @@ #endif class Agent { - public: - Agent(); - Agent(sockaddr *agentPublicSocket, sockaddr *agentLocalSocket, char agentType, uint16_t thisAgentId); - Agent(const Agent &otherAgent); - ~Agent(); - Agent& operator=(Agent otherAgent); - bool operator==(const Agent& otherAgent); - - bool matches(sockaddr *otherPublicSocket, sockaddr *otherLocalSocket, char otherAgentType); - char getType(); - void setType(char newType); - uint16_t getAgentId(); - void setAgentId(uint16_t thisAgentId); - double getFirstRecvTimeUsecs(); - void setFirstRecvTimeUsecs(double newTimeUsecs); - double getLastRecvTimeUsecs(); - void setLastRecvTimeUsecs(double newTimeUsecs); - sockaddr* getPublicSocket(); - void setPublicSocket(sockaddr *newSocket); - sockaddr* getLocalSocket(); - void setLocalSocket(sockaddr *newSocket); - sockaddr* getActiveSocket(); - void activatePublicSocket(); - void activateLocalSocket(); - AgentData* getLinkedData(); - void setLinkedData(AgentData *newData); + void swap(Agent &first, Agent &second); + sockaddr *publicSocket, *localSocket, *activeSocket; + char type; + uint16_t agentId; + double firstRecvTimeUsecs; + double lastRecvTimeUsecs; + AgentData *linkedData; - friend std::ostream& operator<<(std::ostream& os, const Agent* agent); - private: - void swap(Agent &first, Agent &second); - sockaddr *publicSocket, *localSocket, *activeSocket; - char type; - uint16_t agentId; - double firstRecvTimeUsecs; - double lastRecvTimeUsecs; - AgentData *linkedData; +public: + Agent(); + Agent(sockaddr *agentPublicSocket, sockaddr *agentLocalSocket, char agentType, uint16_t thisAgentId); + Agent(const Agent &otherAgent); + ~Agent(); + Agent& operator=(Agent otherAgent); + bool operator==(const Agent& otherAgent); + + bool matches(sockaddr *otherPublicSocket, sockaddr *otherLocalSocket, char otherAgentType); + + pthread_mutex_t deleteMutex; + + char getType(); + void setType(char newType); + uint16_t getAgentId(); + void setAgentId(uint16_t thisAgentId); + double getFirstRecvTimeUsecs(); + void setFirstRecvTimeUsecs(double newTimeUsecs); + double getLastRecvTimeUsecs(); + void setLastRecvTimeUsecs(double newTimeUsecs); + sockaddr* getPublicSocket(); + void setPublicSocket(sockaddr *newSocket); + sockaddr* getLocalSocket(); + void setLocalSocket(sockaddr *newSocket); + sockaddr* getActiveSocket(); + void activatePublicSocket(); + void activateLocalSocket(); + AgentData* getLinkedData(); + void setLinkedData(AgentData *newData); + + friend std::ostream& operator<<(std::ostream& os, const Agent* agent); }; std::ostream& operator<<(std::ostream& os, const Agent* agent); diff --git a/shared/src/AgentList.cpp b/shared/src/AgentList.cpp index 00ba1a9207..26ec2744e3 100644 --- a/shared/src/AgentList.cpp +++ b/shared/src/AgentList.cpp @@ -19,8 +19,8 @@ #endif const char * SOLO_AGENT_TYPES_STRING = "MV"; -char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; -char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup +char DOMAIN_HOSTNAME[] = "highfidelity.below92.commm"; +char DOMAIN_IP[100] = "192.168.0.6"; // IP Address will be re-set by lookup on startup const int DOMAINSERVER_PORT = 40102; bool silentAgentThreadStopFlag = false; @@ -67,28 +67,24 @@ unsigned int AgentList::getSocketListenPort() { void AgentList::processAgentData(sockaddr *senderAddress, void *packetData, size_t dataBytes) { switch (((char *)packetData)[0]) { - case 'D': - { + case 'D': { // list of agents from domain server updateList((unsigned char *)packetData, dataBytes); break; } - case 'H': - { + case 'H': { // head data from another agent updateAgentWithData(senderAddress, packetData, dataBytes); break; } - case 'P': - { + case 'P': { // ping from another agent //std::cout << "Got ping from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n"; char reply[] = "R"; agentSocket.send(senderAddress, reply, 1); break; } - case 'R': - { + case 'R': { // ping reply from another agent //std::cout << "Got ping reply from " << inet_ntoa(((sockaddr_in *)senderAddress)->sin_addr) << "\n"; handlePingReply(senderAddress); @@ -264,11 +260,22 @@ void *removeSilentAgents(void *args) { checkTimeUSecs = usecTimestampNow(); for(std::vector::iterator agent = agents->begin(); agent != agents->end();) { - if ((checkTimeUSecs - agent->getLastRecvTimeUsecs()) > AGENT_SILENCE_THRESHOLD_USECS && agent->getType() != 'V') { + + pthread_mutex_t * agentDeleteMutex = &agent->deleteMutex; + + if ((checkTimeUSecs - agent->getLastRecvTimeUsecs()) > AGENT_SILENCE_THRESHOLD_USECS && agent->getType() != 'V' + && pthread_mutex_trylock(agentDeleteMutex) == 0) { + std::cout << "Killing agent " << &(*agent) << "\n"; + + // make sure the vector isn't currently adding an agent pthread_mutex_lock(&vectorChangeMutex); agent = agents->erase(agent); pthread_mutex_unlock(&vectorChangeMutex); + + // release the delete mutex and destroy it + pthread_mutex_unlock(agentDeleteMutex); + pthread_mutex_destroy(agentDeleteMutex); } else { agent++; } diff --git a/shared/src/AgentList.h b/shared/src/AgentList.h index 4e313a2e4a..34a927b1fc 100644 --- a/shared/src/AgentList.h +++ b/shared/src/AgentList.h @@ -57,7 +57,6 @@ public: void processAgentData(sockaddr *senderAddress, void *packetData, size_t dataBytes); void updateAgentWithData(sockaddr *senderAddress, void *packetData, size_t dataBytes); void broadcastToAgents(char *broadcastData, size_t dataBytes); - void sendToAgent(Agent *destAgent, void *packetData, size_t dataBytes); void pingAgents(); char getOwnerType(); unsigned int getSocketListenPort(); diff --git a/shared/src/AudioRingBuffer.cpp b/shared/src/AudioRingBuffer.cpp index 6dd18de520..73103f8a85 100644 --- a/shared/src/AudioRingBuffer.cpp +++ b/shared/src/AudioRingBuffer.cpp @@ -117,9 +117,6 @@ void AudioRingBuffer::parseData(void *data, int size) { dataPtr += sizeof(float); } - unsigned int attenuationByte = *(dataPtr++); - attenuationRatio = attenuationByte / 255.0f; - memcpy(&bearing, dataPtr, sizeof(float)); dataPtr += sizeof(float); diff --git a/shared/src/SharedUtil.cpp b/shared/src/SharedUtil.cpp index 6f1ae3fa90..5d1072ff57 100644 --- a/shared/src/SharedUtil.cpp +++ b/shared/src/SharedUtil.cpp @@ -29,6 +29,10 @@ float randFloat () { return (rand() % 10000)/10000.f; } +int randIntInRange (int min, int max) { + return min + (rand() % (max - min)); +} + float randFloatInRange (float min,float max) { return min + ((rand() % 10000)/10000.f * (max-min)); } @@ -89,14 +93,11 @@ void switchToResourcesIfRequired() { // then you're using the "-i" flag to set the input file name. // Usage: char * inputFilename = getCmdOption(argc, argv, "-i"); // Complaints: Brad :) -char* getCmdOption(int argc, char** argv,char* option) -{ +const char* getCmdOption(int argc, const char * argv[],const char* option) { // check each arg - for (int i=0; i < argc; i++) - { + for (int i=0; i < argc; i++) { // if the arg matches the desired option - if (strcmp(option,argv[i])==0 && i+1 < argc) - { + if (strcmp(option,argv[i])==0 && i+1 < argc) { // then return the next option return argv[i+1]; } @@ -110,14 +111,12 @@ char* getCmdOption(int argc, char** argv,char* option) // included while launching the application. Returns bool true/false // Usage: bool wantDump = cmdOptionExists(argc, argv, "-d"); // Complaints: Brad :) -bool cmdOptionExists(int argc, char** argv,char* option) -{ + +bool cmdOptionExists(int argc, const char * argv[],const char* option) { // check each arg - for (int i=0; i < argc; i++) - { + for (int i=0; i < argc; i++) { // if the arg matches the desired option - if (strcmp(option,argv[i])==0) - { + if (strcmp(option,argv[i])==0) { // then return the next option return true; } @@ -172,13 +171,11 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, // Now we actually fill out the voxel code while (octetsDone < voxelSizeInOctets) { - if (x > xTest) { // byte = (byte << 1) | true; xTest += sTest/2.0; - } - else { + } else { // byte = (byte << 1) | false; xTest -= sTest/2.0; @@ -186,8 +183,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, 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) - { + if (bitInByteNDX > 7) { voxelOut[byteNDX]=byte; byteNDX++; bitInByteNDX=0; @@ -198,8 +194,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, // byte = (byte << 1) | true; yTest += sTest/2.0; - } - else { + } else { // byte = (byte << 1) | false; yTest -= sTest/2.0; @@ -207,8 +202,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, 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) - { + if (bitInByteNDX > 7) { voxelOut[byteNDX]=byte; byteNDX++; bitInByteNDX=0; @@ -219,8 +213,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, // byte = (byte << 1) | true; zTest += sTest/2.0; - } - else { + } else { // byte = (byte << 1) | false; zTest -= sTest/2.0; @@ -228,8 +221,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, 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) - { + if (bitInByteNDX > 7) { voxelOut[byteNDX]=byte; byteNDX++; bitInByteNDX=0; @@ -242,11 +234,9 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, // 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) - { + if (bitInByteNDX > 0 && bitInByteNDX < 7) { // Pad the last byte - while (bitInByteNDX <= 7) - { + while (bitInByteNDX <= 7) { byte = (byte << 1) | false; bitInByteNDX++; } @@ -263,8 +253,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, return voxelOut; } -void printVoxelCode(unsigned char* voxelCode) -{ +void printVoxelCode(unsigned char* voxelCode) { unsigned char octets = voxelCode[0]; unsigned int voxelSizeInBits = octets*3; unsigned int voxelSizeInBytes = (voxelSizeInBits/8)+1; @@ -277,8 +266,7 @@ void printVoxelCode(unsigned char* voxelCode) printf("voxelSizeInOctets=%d\n",voxelSizeInOctets); printf("voxelBufferSize=%d\n",voxelBufferSize); - for(int i=0;ioctalCode = childOctalCode(octalCode, childIndex); } +// will average the child colors... void VoxelNode::setColorFromAverageOfChildren(int * colorArray) { if (colorArray == NULL) { colorArray = new int[4]; - memset(colorArray, 0, 4); + memset(colorArray, 0, 4*sizeof(int)); for (int i = 0; i < 8; i++) { if (children[i] != NULL && children[i]->color[3] == 1) { for (int j = 0; j < 3; j++) { colorArray[j] += children[i]->color[j]; } - colorArray[3]++; } } @@ -62,14 +60,54 @@ void VoxelNode::setColorFromAverageOfChildren(int * colorArray) { // set the average color value color[c] = colorArray[c] / colorArray[3]; } - // set the alpha to 1 to indicate that this isn't transparent color[3] = 1; } else { // some children, but not enough // set this node's alpha to 0 color[3] = 0; - } + } +} + +// will detect if children are leaves AND the same color +// and in that case will delete the children and make this node +// a leaf, returns TRUE if all the leaves are collapsed into a +// single node +bool VoxelNode::collapseIdenticalLeaves() { + // scan children, verify that they are ALL present and accounted for + bool allChildrenMatch = true; // assume the best (ottimista) + int red,green,blue; + for (int i = 0; i < 8; i++) { + // if no child, or child doesn't have a color + if (children[i] == NULL || children[i]->color[3] != 1) { + allChildrenMatch=false; + //printf("SADNESS child missing or not colored! i=%d\n",i); + break; + } else { + if (i==0) { + red = children[i]->color[0]; + green = children[i]->color[1]; + blue = children[i]->color[2]; + } else if (red != children[i]->color[0] || green != children[i]->color[1] || blue != children[i]->color[2]) { + allChildrenMatch=false; + break; + } + } + } + + + if (allChildrenMatch) { + //printf("allChildrenMatch: pruning tree\n"); + for (int i = 0; i < 8; i++) { + delete children[i]; // delete all the child nodes + children[i]=NULL; // set it to NULL + } + color[0]=red; + color[1]=green; + color[2]=blue; + color[3]=1; // color is set + } + return allChildrenMatch; } void VoxelNode::setRandomColor(int minimumBrightness) { diff --git a/shared/src/VoxelNode.h b/shared/src/VoxelNode.h index bdd7428f6d..9be1b64c70 100644 --- a/shared/src/VoxelNode.h +++ b/shared/src/VoxelNode.h @@ -19,11 +19,11 @@ public: void addChildAtIndex(int childIndex); void setColorFromAverageOfChildren(int * colorArray = NULL); void setRandomColor(int minimumBrightness); + bool collapseIdenticalLeaves(); unsigned char *octalCode; unsigned char color[4]; VoxelNode *children[8]; - unsigned char childMask; }; #endif /* defined(__hifi__VoxelNode__) */ diff --git a/shared/src/VoxelTree.cpp b/shared/src/VoxelTree.cpp index ca76f4067a..4ccb2dffd7 100644 --- a/shared/src/VoxelTree.cpp +++ b/shared/src/VoxelTree.cpp @@ -6,16 +6,45 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#ifdef _WIN32 +#define _USE_MATH_DEFINES +#endif #include #include #include "SharedUtil.h" #include "OctalCode.h" #include "VoxelTree.h" +#include // to load voxels from file +#include // to load voxels from file + +int boundaryDistanceForRenderLevel(unsigned int renderLevel) { + switch (renderLevel) { + case 1: + case 2: + case 3: + return 100; + case 4: + return 75; + break; + case 5: + return 50; + break; + case 6: + return 25; + break; + case 7: + return 12; + break; + default: + return 6; + break; + } +} VoxelTree::VoxelTree() { rootNode = new VoxelNode(); rootNode->octalCode = new unsigned char[1]; - *rootNode->octalCode = (char)0; + *rootNode->octalCode = 0; } VoxelTree::~VoxelTree() { @@ -26,22 +55,6 @@ VoxelTree::~VoxelTree() { } } -int VoxelTree::levelForViewerPosition(float *position) { - // get the distance to the viewer - // for now the voxel tree starts at 0,0,0 - float distance = sqrtf(powf(position[0] + 30, 2) + powf(position[2] + 30, 2)); - - // go through the if else branch to return the right level - // this is a gross way to do this for now for a friday demo - if (distance >= 50) { - return 3; - } else if (distance >= 20) { - return 4; - } else { - return 5; - } -} - VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode) { // find the appropriate branch index based on this ancestorNode if (*needleCode > 0) { @@ -77,45 +90,9 @@ VoxelNode * VoxelTree::createMissingNode(VoxelNode *lastParentNode, unsigned cha } } -/// If node has color & <= 4 children then prune children -void VoxelTree::pruneTree(VoxelNode* pruneAt) { - int childCount = 0; - - // determine how many children we have - for (int i = 0; i < 8; i++) - { - if (pruneAt->children[i]) - { - childCount++; - } - } - // if appropriate, prune them - if (pruneAt->color[3] && childCount <= 4) - { - for (int i = 0; i < 8; i++) - { - if (pruneAt->children[i]) - { - delete pruneAt->children[i]; - pruneAt->children[i]=NULL; - } - } - } - else - { - // Otherwise, iterate the children and recursively call - // prune on all of them - for (int i = 0; i < 8; i++) - { - if (pruneAt->children[i]) - { - this->pruneTree(pruneAt->children[i]); - } - } - } -} - -int VoxelTree::readNodeData(VoxelNode *destinationNode, unsigned char * nodeData, int bytesLeftToRead) { +int VoxelTree::readNodeData(VoxelNode *destinationNode, + unsigned char * nodeData, + int bytesLeftToRead) { // instantiate variable for bytes already read int bytesRead = 1; @@ -147,7 +124,7 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode, unsigned char * nodeData destinationNode->setColorFromAverageOfChildren(colorArray); // give this destination node the child mask from the packet - destinationNode->childMask = *(nodeData + bytesRead); + unsigned char childMask = *(nodeData + bytesRead); int childIndex = 0; bytesRead++; @@ -155,14 +132,16 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode, unsigned char * nodeData while (bytesLeftToRead - bytesRead > 0 && childIndex < 8) { // check the exists mask to see if we have a child to traverse into - if (oneAtBit(destinationNode->childMask, childIndex)) { + if (oneAtBit(childMask, childIndex)) { if (destinationNode->children[childIndex] == NULL) { // add a child at that index, if it doesn't exist destinationNode->addChildAtIndex(childIndex); } // tell the child to read the subsequent data - bytesRead += readNodeData(destinationNode->children[childIndex], nodeData + bytesRead, bytesLeftToRead - bytesRead); + bytesRead += readNodeData(destinationNode->children[childIndex], + nodeData + bytesRead, + bytesLeftToRead - bytesRead); } childIndex++; @@ -185,7 +164,6 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeByt } void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { - int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer); VoxelNode *lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer); // create the node if it does not exist @@ -195,102 +173,170 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) { } // give this node its color + int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer); memcpy(lastCreatedNode->color, codeColorBuffer + octalCodeBytes, 3); lastCreatedNode->color[3] = 1; } unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer, - unsigned char * stopOctalCode, VoxelNode *currentVoxelNode, - int deepestLevel) + MarkerNode *currentMarkerNode, + float * agentPosition, + float thisNodePosition[3], + unsigned char * stopOctalCode) { static unsigned char *initialBitstreamPos = bitstreamBuffer; - int firstIndexToCheck = 0; - - // we'll only be writing data if we're lower than - // or at the same level as the stopOctalCode - if (*currentVoxelNode->octalCode >= *stopOctalCode) { - if ((bitstreamBuffer - initialBitstreamPos) + MAX_TREE_SLICE_BYTES > MAX_VOXEL_PACKET_SIZE) { - // we can't send this packet, not enough room - // return our octal code as the stop - return currentVoxelNode->octalCode; - } - - if (strcmp((char *)stopOctalCode, (char *)currentVoxelNode->octalCode) == 0) { - // this is is the root node for this packet - // add the leading V - *(bitstreamBuffer++) = 'V'; - - // add its octal code to the packet - int octalCodeBytes = bytesRequiredForCodeLength(*currentVoxelNode->octalCode); - - memcpy(bitstreamBuffer, currentVoxelNode->octalCode, octalCodeBytes); - bitstreamBuffer += octalCodeBytes; - } - - // default color mask is 0, increment pointer for colors - *bitstreamBuffer = 0; - - // keep a colorPointer so we can check how many colors were added - unsigned char *colorPointer = bitstreamBuffer + 1; - - for (int i = 0; i < 8; i++) { - - // check if the child exists and is not transparent - if (currentVoxelNode->children[i] != NULL - && currentVoxelNode->children[i]->color[3] != 0) { - - // copy in the childs color to bitstreamBuffer - memcpy(colorPointer, currentVoxelNode->children[i]->color, 3); - colorPointer += 3; - - // set the colorMask by bitshifting the value of childExists - *bitstreamBuffer += (1 << (7 - i)); - } - } - - // push the bitstreamBuffer forwards for the number of added colors - bitstreamBuffer += (colorPointer - bitstreamBuffer); - - // copy the childMask to the current position of the bitstreamBuffer - // and push the buffer pointer forwards - *(bitstreamBuffer++) = *currentVoxelNode->octalCode < deepestLevel - 1 - ? currentVoxelNode->childMask - : 0; - } else { - firstIndexToCheck = *stopOctalCode > 0 - ? branchIndexWithDescendant(currentVoxelNode->octalCode, stopOctalCode) - : 0; - } - unsigned char * childStopOctalCode = NULL; - if (currentVoxelNode->childMask == 0) { - leavesWrittenToBitstream++; + if (stopOctalCode == NULL) { + stopOctalCode = rootNode->octalCode; } - if (*currentVoxelNode->octalCode < deepestLevel - 1) { - for (int i = firstIndexToCheck; i < 8; i ++) { + // check if we have any children + bool hasAtLeastOneChild; + + for (int i = 0; i < 8; i++) { + if (currentVoxelNode->children[i] != NULL) { + hasAtLeastOneChild = true; + } + } + + // if we have at least one child, check if it will be worth recursing into our children + if (hasAtLeastOneChild) { + + int firstIndexToCheck = 0; + unsigned char * childMaskPointer = NULL; + + float halfUnitForVoxel = powf(0.5, *currentVoxelNode->octalCode) * (0.5 * TREE_SCALE); + + float distanceToVoxelCenter = sqrtf(powf(agentPosition[0] - thisNodePosition[0] - halfUnitForVoxel, 2) + + powf(agentPosition[1] - thisNodePosition[1] - halfUnitForVoxel, 2) + + powf(agentPosition[2] - thisNodePosition[2] - halfUnitForVoxel, 2)); + + // 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)) { - // ask the child to load this bitstream buffer - // if they or their descendants fill the MTU we will receive the childStopOctalCode back - if (currentVoxelNode->children[i] != NULL) { - if (*currentVoxelNode->octalCode < *stopOctalCode - && i > firstIndexToCheck - && childStopOctalCode == NULL) { - return currentVoxelNode->children[i]->octalCode; - } else { - if (oneAtBit(currentVoxelNode->childMask, i)) { - childStopOctalCode = loadBitstreamBuffer(bitstreamBuffer, stopOctalCode, currentVoxelNode->children[i], deepestLevel); - } else { - childStopOctalCode = NULL; + // write this voxel's data if we're below or at + // or at the same level as the stopOctalCode + + if (*currentVoxelNode->octalCode >= *stopOctalCode) { + if ((bitstreamBuffer - initialBitstreamPos) + MAX_TREE_SLICE_BYTES > MAX_VOXEL_PACKET_SIZE) { + // we can't send this packet, not enough room + // return our octal code as the stop + return currentVoxelNode->octalCode; + } + + if (strcmp((char *)stopOctalCode, (char *)currentVoxelNode->octalCode) == 0) { + // this is is the root node for this packet + // add the leading V + *(bitstreamBuffer++) = 'V'; + + // add its octal code to the packet + int octalCodeBytes = bytesRequiredForCodeLength(*currentVoxelNode->octalCode); + + memcpy(bitstreamBuffer, currentVoxelNode->octalCode, octalCodeBytes); + bitstreamBuffer += octalCodeBytes; + } + + // default color mask is 0, increment pointer for colors + *bitstreamBuffer = 0; + + // keep a colorPointer so we can check how many colors were added + unsigned char *colorPointer = bitstreamBuffer + 1; + + for (int i = 0; i < 8; i++) { + + // check if the child exists and is not transparent + if (currentVoxelNode->children[i] != NULL + && currentVoxelNode->children[i]->color[3] != 0) { + + // copy in the childs color to bitstreamBuffer + memcpy(colorPointer, currentVoxelNode->children[i]->color, 3); + colorPointer += 3; + + // set the colorMask by bitshifting the value of childExists + *bitstreamBuffer += (1 << (7 - i)); } } - } + + // push the bitstreamBuffer forwards for the number of added colors + bitstreamBuffer += (colorPointer - bitstreamBuffer); + + // maintain a pointer to this spot in the buffer so we can set our child mask + // depending on the results of the recursion below + childMaskPointer = bitstreamBuffer++; + + // reset the childMaskPointer for this node to 0 + *childMaskPointer = 0; + } else { + firstIndexToCheck = *stopOctalCode > 0 + ? branchIndexWithDescendant(currentVoxelNode->octalCode, stopOctalCode) + : 0; + } - if (childStopOctalCode != NULL) { - break; + unsigned char * arrBufferBeforeChild = bitstreamBuffer; + + for (int i = firstIndexToCheck; i < 8; i ++) { + + // ask the child to load this bitstream buffer + // if they or their descendants fill the MTU we will receive the childStopOctalCode back + if (currentVoxelNode->children[i] != NULL) { + + if (!oneAtBit(currentMarkerNode->childrenVisitedMask, i)) { + + // create the marker node for this child if it does not yet exist + if (currentMarkerNode->children[i] == NULL) { + currentMarkerNode->children[i] = new MarkerNode(); + } + + // calculate the child's position based on the parent position + float childNodePosition[3]; + + for (int j = 0; j < 3; j++) { + childNodePosition[j] = thisNodePosition[j]; + + if (oneAtBit(branchIndexWithDescendant(currentVoxelNode->octalCode, + currentVoxelNode->children[i]->octalCode), + (7 - j))) { + childNodePosition[j] -= (powf(0.5, *currentVoxelNode->children[i]->octalCode) * TREE_SCALE); + } + } + + // ask the child to load the bitstream buffer with their data + childStopOctalCode = loadBitstreamBuffer(bitstreamBuffer, + currentVoxelNode->children[i], + currentMarkerNode->children[i], + agentPosition, + childNodePosition, + stopOctalCode); + + if (bitstreamBuffer - arrBufferBeforeChild > 0) { + // this child added data to the packet - add it to our child mask + if (childMaskPointer != NULL) { + *childMaskPointer += (1 << (7 - i)); + } + + arrBufferBeforeChild = bitstreamBuffer; + } + } + } + + if (childStopOctalCode != NULL) { + break; + } else { + // this child node has been covered + // add the appropriate bit to the childrenVisitedMask for the current marker node + currentMarkerNode->childrenVisitedMask += 1 << (7 - i); + + // if we are above the stopOctal and we got a NULL code + // we cannot go to the next child + // so break and return the NULL stop code + if (*currentVoxelNode->octalCode < *stopOctalCode) { + break; + } + } } } } @@ -319,13 +365,186 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { } } - outputBits(startNode->childMask); + unsigned char childMask = 0; - // 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) { - printTreeForDebugging(startNode->children[k]); + childMask += (1 << (7 - k)); } } + + outputBits(childMask); + + if (childMask > 0) { + // ask children to recursively output their trees + // if they aren't a leaf + for (int l = 0; l < 8; l++) { + if (startNode->children[l] != NULL) { + printTreeForDebugging(startNode->children[l]); + } + } + } +} + +void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { + bool hasChildren = false; + + for (int i = 0; i < 8; i++) { + if (startNode->children[i] != NULL) { + reaverageVoxelColors(startNode->children[i]); + hasChildren = true; + } + } + + if (hasChildren) { + bool childrenCollapsed = startNode->collapseIdenticalLeaves(); + + if (!childrenCollapsed) { + startNode->setColorFromAverageOfChildren(); + } + } + +} +////////////////////////////////////////////////////////////////////////////////////////// +// Method: VoxelTree::loadVoxelsFile() +// Description: Loads HiFidelity encoded Voxels from a binary file. The current file +// format is a stream of single voxels with color data. +// Complaints: Brad :) +void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { + int vCount = 0; + + std::ifstream file(fileName, std::ios::in|std::ios::binary); + + char octets; + unsigned int lengthInBytes; + + int totalBytesRead = 0; + if(file.is_open()) { + printf("loading file...\n"); + bool bail = false; + while (!file.eof() && !bail) { + file.get(octets); + //printf("octets=%d...\n",octets); + totalBytesRead++; + lengthInBytes = bytesRequiredForCodeLength(octets)-1; //(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; + } + // read color data + char colorRead; + unsigned char red,green,blue; + file.get(colorRead); + red = (unsigned char)colorRead; + file.get(colorRead); + green = (unsigned char)colorRead; + file.get(colorRead); + blue = (unsigned char)colorRead; + + printf("voxel color from file red:%d, green:%d, blue:%d \n",red,green,blue); + vCount++; + + int colorRandomizer = wantColorRandomizer ? randIntInRange (-5, 5) : 0; + voxelData[lengthInBytes+1] = std::max(0,std::min(255,red + colorRandomizer)); + voxelData[lengthInBytes+2] = std::max(0,std::min(255,green + colorRandomizer)); + voxelData[lengthInBytes+3] = std::max(0,std::min(255,blue + colorRandomizer)); + printf("voxel color after rand red:%d, green:%d, blue:%d\n", + voxelData[lengthInBytes+1], voxelData[lengthInBytes+2], voxelData[lengthInBytes+3]); + + //printVoxelCode(voxelData); + this->readCodeColorBufferToTree(voxelData); + delete voxelData; + } + file.close(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// Method: VoxelTree::createSphere() +// Description: Creates a sphere of voxels in the local system at a given location/radius +// To Do: Move this function someplace better? +// Complaints: Brad :) +void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) { + // 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 dominantColor1 = randIntInRange(1,3); //1=r, 2=g, 3=b dominant + unsigned char dominantColor2 = randIntInRange(1,3); + + if (dominantColor1==dominantColor2) { + dominantColor2 = dominantColor1+1%3; + } + + unsigned char r1 = (dominantColor1==1)?randIntInRange(200,255):randIntInRange(40,100); + unsigned char g1 = (dominantColor1==2)?randIntInRange(200,255):randIntInRange(40,100); + unsigned char b1 = (dominantColor1==3)?randIntInRange(200,255):randIntInRange(40,100); + unsigned char r2 = (dominantColor2==1)?randIntInRange(200,255):randIntInRange(40,100); + unsigned char g2 = (dominantColor2==2)?randIntInRange(200,255):randIntInRange(40,100); + unsigned char b2 = (dominantColor2==3)?randIntInRange(200,255):randIntInRange(40,100); + + // We initialize our rgb to be either "grey" in case of randomized surface, or + // the average of the gradient, in the case of the gradient sphere. + unsigned char red = wantColorRandomizer ? 128 : (r1+r2)/2; // average of the colors + unsigned char green = wantColorRandomizer ? 128 : (g1+g2)/2; + unsigned char blue = wantColorRandomizer ? 128 : (b1+b2)/2; + + // 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+(s/2.0)); ri+=s) { + //printf("radius: ri=%f ri+s=%f (r+(s/2.0))=%f\n",ri,ri+s,(r+(s/2.0))); + 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+ri*cos(theta)*sin(phi); + float y = yc+ri*sin(theta)*sin(phi); + float z = zc+ri*cos(phi); + + // gradient color data + float gradient = (phi/M_PI); + + // only use our actual desired color on the outer edge, otherwise + // use our "average" color + if (ri+(s*2.0)>=r) { + //printf("painting candy shell radius: ri=%f r=%f\n",ri,r); + red = wantColorRandomizer ? randomColorValue(165) : r1+((r2-r1)*gradient); + green = wantColorRandomizer ? randomColorValue(165) : g1+((g2-g1)*gradient); + blue = wantColorRandomizer ? randomColorValue(165) : b1+((b2-b1)*gradient); + } + + unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); + this->readCodeColorBufferToTree(voxelData); + //printf("voxel data for x:%f y:%f z:%f s:%f\n",x,y,z,s); + //printVoxelCode(voxelData); + delete voxelData; + } + } + } + this->reaverageVoxelColors(this->rootNode); } \ No newline at end of file diff --git a/shared/src/VoxelTree.h b/shared/src/VoxelTree.h index bea2533543..a265b9fa9e 100644 --- a/shared/src/VoxelTree.h +++ b/shared/src/VoxelTree.h @@ -11,9 +11,11 @@ #include #include "VoxelNode.h" +#include "MarkerNode.h" const int MAX_VOXEL_PACKET_SIZE = 1492; const int MAX_TREE_SLICE_BYTES = 26; +const int TREE_SCALE = 10; class VoxelTree { VoxelNode * nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode); @@ -26,16 +28,21 @@ public: VoxelNode *rootNode; int leavesWrittenToBitstream; - int levelForViewerPosition(float * position); void readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes); void readCodeColorBufferToTree(unsigned char *codeColorBuffer); - unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer, - unsigned char * octalCode, - VoxelNode *currentVoxelNode, - int deepestLevel); void printTreeForDebugging(VoxelNode *startNode); - - void pruneTree(VoxelNode* pruneAt); + void reaverageVoxelColors(VoxelNode *startNode); + unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer, + VoxelNode *currentVoxelNode, + MarkerNode *currentMarkerNode, + float * agentPosition, + float thisNodePosition[3], + unsigned char * octalCode = NULL); + + void loadVoxelsFile(const char* fileName, bool wantColorRandomizer); + void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer); }; +int boundaryDistanceForRenderLevel(unsigned int renderLevel); + #endif /* defined(__hifi__VoxelTree__) */ diff --git a/voxel/src/VoxelAgentData.cpp b/voxel/src/VoxelAgentData.cpp index 98311fdbac..e54ce0df89 100644 --- a/voxel/src/VoxelAgentData.cpp +++ b/voxel/src/VoxelAgentData.cpp @@ -11,7 +11,6 @@ #include VoxelAgentData::VoxelAgentData() { - lastSentLevel = 0; rootMarkerNode = new MarkerNode(); } @@ -20,9 +19,8 @@ VoxelAgentData::~VoxelAgentData() { } VoxelAgentData::VoxelAgentData(const VoxelAgentData &otherAgentData) { - lastSentLevel = otherAgentData.lastSentLevel; memcpy(position, otherAgentData.position, sizeof(float) * 3); - rootMarkerNode = new MarkerNode(*otherAgentData.rootMarkerNode); + rootMarkerNode = new MarkerNode(); } VoxelAgentData* VoxelAgentData::clone() const { diff --git a/voxel/src/VoxelAgentData.h b/voxel/src/VoxelAgentData.h index 21b521126d..3ab7670a93 100644 --- a/voxel/src/VoxelAgentData.h +++ b/voxel/src/VoxelAgentData.h @@ -16,7 +16,6 @@ class VoxelAgentData : public AgentData { public: float position[3]; - int lastSentLevel; MarkerNode *rootMarkerNode; VoxelAgentData(); diff --git a/voxel/src/main.cpp b/voxel/src/main.cpp index 9dcb8d6182..e1c630bbd1 100644 --- a/voxel/src/main.cpp +++ b/voxel/src/main.cpp @@ -39,48 +39,72 @@ const int MIN_BRIGHTNESS = 64; const float DEATH_STAR_RADIUS = 4.0; const float MAX_CUBE = 0.05f; -const int MAX_VOXEL_TREE_DEPTH_LEVELS = 10; +const int VOXEL_SEND_INTERVAL_USECS = 30 * 1000; +const int PACKETS_PER_CLIENT_PER_INTERVAL = 3; + +const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4; AgentList agentList('V', VOXEL_LISTEN_PORT); +VoxelTree randomTree; -int randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { +void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) { + float r = random ? randFloatInRange(0.05,0.1) : 0.25; + float xc = random ? randFloatInRange(r,(1-r)) : 0.5; + float yc = random ? randFloatInRange(r,(1-r)) : 0.5; + float zc = random ? randFloatInRange(r,(1-r)) : 0.5; + float s = (1.0/256); // size of voxels to make up surface of sphere + bool solid = true; + + printf("adding sphere:"); + if (random) + printf(" random"); + printf("\nradius=%f\n",r); + printf("xc=%f\n",xc); + printf("yc=%f\n",yc); + printf("zc=%f\n",zc); + + tree->createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer); +} + +void addSphereScene(VoxelTree * tree, bool wantColorRandomizer) { + printf("adding scene of spheres...\n"); + tree->createSphere(0.25,0.5,0.5,0.5,(1.0/256),true,wantColorRandomizer); + tree->createSphere(0.030625,0.5,0.5,(0.25-0.06125),(1.0/512),true,true); +} + + +void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { // randomly generate children for this node // the first level of the tree (where levelsToGo = MAX_VOXEL_TREE_DEPTH_LEVELS) has all 8 if (levelsToGo > 0) { - int grandChildrenFromNode = 0; bool createdChildren = false; int colorArray[4] = {}; - + createdChildren = false; - + for (int i = 0; i < 8; i++) { - if (((i == 0 || i == 1 | i == 4 | i == 5) && (randomBoolean() || levelsToGo != 1)) ) { + if (true) { // create a new VoxelNode to put here currentRootNode->children[i] = new VoxelNode(); - + // give this child it's octal code currentRootNode->children[i]->octalCode = childOctalCode(currentRootNode->octalCode, i); - - // fill out the lower levels of the tree using that node as the root node - grandChildrenFromNode = randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->children[i]); + + randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->children[i]); if (currentRootNode->children[i]->color[3] == 1) { for (int c = 0; c < 3; c++) { colorArray[c] += currentRootNode->children[i]->color[c]; } - + colorArray[3]++; } - - if (grandChildrenFromNode > 0) { - currentRootNode->childMask += (1 << (7 - i)); - } - + createdChildren = true; } } - + if (!createdChildren) { // we didn't create any children for this node, making it a leaf // give it a random color @@ -89,16 +113,88 @@ int randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) { // set the color value for this node currentRootNode->setColorFromAverageOfChildren(colorArray); } - - return createdChildren; } else { // this is a leaf node, just give it a color currentRootNode->setRandomColor(MIN_BRIGHTNESS); - - return 0; } } +void *distributeVoxelsToListeners(void *args) { + + timeval lastSendTime; + + unsigned char *stopOctal; + int packetCount; + + int totalBytesSent; + + unsigned char *voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; + unsigned char *voxelPacketEnd; + + float treeRoot[3] = {0, 0, 0}; + + while (true) { + gettimeofday(&lastSendTime, NULL); + + // enumerate the agents to send 3 packets to each + for (int i = 0; i < agentList.getAgents().size(); i++) { + + Agent *thisAgent = (Agent *)&agentList.getAgents()[i]; + VoxelAgentData *agentData = (VoxelAgentData *)(thisAgent->getLinkedData()); + + // 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); + + stopOctal = NULL; + packetCount = 0; + totalBytesSent = 0; + randomTree.leavesWrittenToBitstream = 0; + + for (int j = 0; j < PACKETS_PER_CLIENT_PER_INTERVAL; j++) { + voxelPacketEnd = voxelPacket; + stopOctal = randomTree.loadBitstreamBuffer(voxelPacketEnd, + randomTree.rootNode, + agentData->rootMarkerNode, + agentData->position, + treeRoot, + stopOctal); + + agentList.getAgentSocket().send(thisAgent->getActiveSocket(), voxelPacket, voxelPacketEnd - voxelPacket); + + packetCount++; + totalBytesSent += voxelPacketEnd - voxelPacket; + + if (agentData->rootMarkerNode->childrenVisitedMask == 255) { + break; + } + } + + // for any agent that has a root marker node with 8 visited children + // recursively delete its marker nodes so we can revisit + if (agentData->rootMarkerNode->childrenVisitedMask == 255) { + delete agentData->rootMarkerNode; + agentData->rootMarkerNode = new MarkerNode(); + } + + // unlock the delete mutex so the other thread can + // kill the agent if it has dissapeared + pthread_mutex_unlock(&thisAgent->deleteMutex); + } + + // dynamically sleep until we need to fire off the next set of voxels + double usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + + pthread_exit(0); +} + void attachVoxelAgentDataToAgent(Agent *newAgent) { if (newAgent->getLinkedData() == NULL) { newAgent->setLinkedData(new VoxelAgentData()); @@ -110,26 +206,58 @@ int main(int argc, const char * argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); + // Handle Local Domain testing with the --local command line + const char* local = "--local"; + bool wantLocalDomain = cmdOptionExists(argc, argv,local); + if (wantLocalDomain) { + printf("Local Domain MODE!\n"); + int ip = getLocalAddress(); + sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + } + agentList.linkedDataCreateCallback = &attachVoxelAgentDataToAgent; agentList.startSilentAgentRemovalThread(); agentList.startDomainServerCheckInThread(); srand((unsigned)time(0)); + + // 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"; + bool wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); - // use our method to create a random voxel tree - VoxelTree randomTree; + printf("wantColorRandomizer=%s\n",(wantColorRandomizer?"yes":"no")); + const char* voxelsFilename = getCmdOption(argc, argv, INPUT_FILE); + + if (voxelsFilename) { + randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer); + } + + const char* ADD_RANDOM_VOXELS="--AddRandomVoxels"; + if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) { + // create an octal code buffer and load it with 0 so that the recursive tree fill can give + // octal codes to the tree nodes that it is creating + randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, randomTree.rootNode); + } - // create an octal code buffer and load it with 0 so that the recursive tree fill can give - // octal codes to the tree nodes that it is creating - randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, randomTree.rootNode); - - unsigned char *voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; - unsigned char *voxelPacketEnd; - - unsigned char *stopOctal; - int packetCount; - int totalBytesSent; + + const char* ADD_SPHERE="--AddSphere"; + const char* ADD_RANDOM_SPHERE="--AddRandomSphere"; + if (cmdOptionExists(argc, argv, ADD_SPHERE)) { + addSphere(&randomTree,false,wantColorRandomizer); + } else if (cmdOptionExists(argc, argv, ADD_RANDOM_SPHERE)) { + addSphere(&randomTree,true,wantColorRandomizer); + } + const char* NO_ADD_SCENE="--NoAddScene"; + if (!cmdOptionExists(argc, argv, NO_ADD_SCENE)) { + addSphereScene(&randomTree,wantColorRandomizer); + } + + pthread_t sendVoxelThread; + pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL); + sockaddr agentPublicAddress; char *packetData = new char[MAX_PACKET_SIZE]; @@ -138,17 +266,36 @@ int main(int argc, const char * argv[]) // loop to send to agents requesting data while (true) { if (agentList.getAgentSocket().receive(&agentPublicAddress, packetData, &receivedBytes)) { + // XXXBHG: Hacked in support for 'I' insert command + if (packetData[0] == 'I') { + unsigned short int itemNumber = (*((unsigned short int*)&packetData[1])); + printf("got I command from client receivedBytes=%ld itemNumber=%d\n",receivedBytes,itemNumber); + int atByte = 3; + unsigned char* pVoxelData = (unsigned char*)&packetData[3]; + while (atByte < receivedBytes) { + unsigned char octets = (unsigned char)*pVoxelData; + int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color! + randomTree.readCodeColorBufferToTree(pVoxelData); + //printf("readCodeColorBufferToTree() of size=%d atByte=%d receivedBytes=%ld\n",voxelDataSize,atByte,receivedBytes); + // skip to next + pVoxelData+=voxelDataSize; + atByte+=voxelDataSize; + } + } if (packetData[0] == 'H') { - - if (agentList.addOrUpdateAgent(&agentPublicAddress, &agentPublicAddress, packetData[0], agentList.getLastAgentId())) { + if (agentList.addOrUpdateAgent(&agentPublicAddress, + &agentPublicAddress, + packetData[0], + agentList.getLastAgentId())) { agentList.increaseAgentId(); } - // parse this agent's position agentList.updateAgentWithData(&agentPublicAddress, (void *)packetData, receivedBytes); } } } + + pthread_join(sendVoxelThread, NULL); return 0; } \ No newline at end of file