diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 49e1f7596c..58a8519b05 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -22,10 +22,12 @@ #include // to load voxels from file #include "VoxelConstants.h" +#include + using voxels_lib::printLog; int boundaryDistanceForRenderLevel(unsigned int renderLevel) { - float voxelSizeScale = 500.0*TREE_SCALE; + float voxelSizeScale = 50000.0f; return voxelSizeScale / powf(2, renderLevel); } @@ -113,9 +115,7 @@ VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char } } -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; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -457,87 +457,144 @@ void VoxelTree::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, r } void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float voxelSize, - bool solid, bool wantColorRandomizer, bool debug) { + bool solid, creationMode mode, bool debug) { - // 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); + bool wantColorRandomizer = (mode == RANDOM); + bool wantNaturalSurface = (mode == NATURAL); + bool wantNaturalColor = (mode == NATURAL); - if (dominantColor1 == dominantColor2) { - dominantColor2 = dominantColor1 + 1 % 3; + // About the color of the sphere... we're going to make this sphere be a mixture of two colors + // in NATURAL mode, those colors will be green dominant and blue dominant. In GRADIENT mode we + // will randomly pick which color family red, green, or blue to be dominant. In RANDOM mode we + // ignore these dominant colors and make every voxel a completely random color. + unsigned char r1, g1, b1, r2, g2, b2; + + if (wantNaturalColor) { + r1 = r2 = b2 = g1 = 0; + b1 = g2 = 255; + } else if (!wantColorRandomizer) { + 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; + } + + r1 = (dominantColor1 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100); + g1 = (dominantColor1 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100); + b1 = (dominantColor1 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100); + r2 = (dominantColor2 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100); + g2 = (dominantColor2 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100); + b2 = (dominantColor2 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100); } - 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. + // 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) + // x = xc+r*cos(theta)*sin(phi) + // y = yc+r*sin(theta)*sin(phi) + // z = zc+r*cos(phi) + + // I want to do something smart like make these inside circles with bigger voxels, but this doesn't seem to work. + float thisVoxelSize = voxelSize; // radius / 2.0f; + float thisRadius = 0.0; + if (!solid) { + thisRadius = radius; // just the outer surface + thisVoxelSize = voxelSize; + } + + // If you also iterate form the interior of the sphere to the radius, making + // larger and larger spheres you'd end up with a solid sphere. And lots of voxels! + float finalRadius = wantNaturalSurface ? radius : (radius + (voxelSize / 2.0)); + bool lastLayer = false; + while (!lastLayer) { + lastLayer = (thisRadius + (voxelSize * 2.0) >= radius); - // assume solid for now - float thisRadius = 0.0; - float thisVoxelSize = radius / 4.0f; - - if (!solid) { - thisRadius = radius; // just the outer surface - thisVoxelSize = voxelSize; - } - - // If you also iterate form the interior of the sphere to the radius, makeing - // larger and larger sphere'voxelSize you'd end up with a solid sphere. And lots of voxels! - while (thisRadius <= (radius + (voxelSize / 2.0))) { - if (debug) { - printLog("radius: thisRadius=%f thisVoxelSize=%f thisRadius+thisVoxelSize=%f (radius+(voxelSize/2.0))=%f\n", - thisRadius, thisVoxelSize, thisRadius+thisVoxelSize, (radius + (voxelSize / 2.0))); - } // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize // 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 = (thisVoxelSize / thisRadius); - for (float theta=0.0; theta <= 2 * M_PI; theta += angleDelta) { - for (float phi=0.0; phi <= M_PI; phi += angleDelta) { - float x = xc + thisRadius * cos(theta) * sin(phi); - float y = yc + thisRadius * sin(theta) * sin(phi); - float z = zc + thisRadius * 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 (thisRadius + (voxelSize * 2.0) >= radius) { - //printLog("painting candy shell radius: thisRadius=%f radius=%f\n",thisRadius,radius); - 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, thisVoxelSize, red, green, blue); - this->readCodeColorBufferToTree(voxelData); - delete voxelData; - } - } + if (debug) { + int percentComplete = 100 * (thisRadius/radius); + printLog("percentComplete=%d\n",percentComplete); + } + + for (float theta=0.0; theta <= 2 * M_PI; theta += angleDelta) { + for (float phi=0.0; phi <= M_PI; phi += angleDelta) { + bool naturalSurfaceRendered = false; + float x = xc + thisRadius * cos(theta) * sin(phi); + float y = yc + thisRadius * sin(theta) * sin(phi); + float z = zc + thisRadius * cos(phi); + + // if we're on the outer radius, then we do a couple of things differently. + // 1) If we're in NATURAL mode we will actually draw voxels from our surface outward (from the surface) up + // some random height. This will give our sphere some contours. + // 2) In all modes, we will use our "outer" color to draw the voxels. Otherwise we will use the average color + if (lastLayer) { + if (false && debug) { + printLog("adding candy shell: theta=%f phi=%f thisRadius=%f radius=%f\n", + theta, phi, thisRadius,radius); + } + switch (mode) { + case RANDOM: { + red = randomColorValue(165); + green = randomColorValue(165); + blue = randomColorValue(165); + } break; + case GRADIENT: { + float gradient = (phi / M_PI); + red = r1 + ((r2 - r1) * gradient); + green = g1 + ((g2 - g1) * gradient); + blue = b1 + ((b2 - b1) * gradient); + } break; + case NATURAL: { + glm::vec3 position = glm::vec3(theta,phi,radius); + float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) + + .125f * glm::perlin(position * 16.f); + float gradient = (1.0f + perlin)/ 2.0f; + red = (unsigned char)std::min(255, std::max(0, (int)(r1 + ((r2 - r1) * gradient)))); + green = (unsigned char)std::min(255, std::max(0, (int)(g1 + ((g2 - g1) * gradient)))); + blue = (unsigned char)std::min(255, std::max(0, (int)(b1 + ((b2 - b1) * gradient)))); + if (debug) { + printLog("perlin=%f gradient=%f color=(%d,%d,%d)\n",perlin, gradient, red, green, blue); + } + } break; + } + if (wantNaturalSurface) { + // for natural surfaces, we will render up to 16 voxel's above the surface of the sphere + glm::vec3 position = glm::vec3(theta,phi,radius); + float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) + + .125f * glm::perlin(position * 16.f); + float gradient = (1.0f + perlin)/ 2.0f; + + int height = (4 * gradient)+1; // make it at least 4 thick, so we get some averaging + float subVoxelScale = thisVoxelSize; + for (int i = 0; i < height; i++) { + x = xc + (thisRadius + i * subVoxelScale) * cos(theta) * sin(phi); + y = yc + (thisRadius + i * subVoxelScale) * sin(theta) * sin(phi); + z = zc + (thisRadius + i * subVoxelScale) * cos(phi); + this->createVoxel(x, y, z, subVoxelScale, red, green, blue); + } + naturalSurfaceRendered = true; + } + } + if (!naturalSurfaceRendered) { + this->createVoxel(x, y, z, thisVoxelSize, red, green, blue); + } + } + } thisRadius += thisVoxelSize; - thisVoxelSize = std::max(voxelSize, thisVoxelSize / 2.0f); - } - this->reaverageVoxelColors(this->rootNode); + thisVoxelSize = std::max(voxelSize, thisVoxelSize / 2.0f); + } + this->reaverageVoxelColors(this->rootNode); } int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) { @@ -923,3 +980,13 @@ void VoxelTree::writeToFileV2(const char* fileName) const { file.close(); } +unsigned long VoxelTree::getVoxelCount() { + unsigned long nodeCount = 0; + recurseTreeWithOperation(countVoxelsOperation, &nodeCount); + return nodeCount; +} + +bool VoxelTree::countVoxelsOperation(VoxelNode* node, void* extraData) { + (*(unsigned long*)extraData)++; + return true; // keep going +} diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 421e14ace7..319b4aed95 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -17,6 +17,7 @@ // Callback function, for recuseTreeWithOperation typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData); +typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; class VoxelTree { public: @@ -49,7 +50,7 @@ public: VoxelNode* getVoxelAt(float x, float y, float z, float s) const; void createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue); void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color); - void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer, bool debug = false); + void createSphere(float r,float xc, float yc, float zc, float s, bool solid, creationMode mode, bool debug = false); void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL); @@ -70,6 +71,8 @@ public: // these will read/write files that match the wireformat, excluding the 'V' leading void writeToFileV2(const char* filename) const; bool readFromFileV2(const char* filename); + + unsigned long getVoxelCount(); private: int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, @@ -79,6 +82,8 @@ private: int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); + static bool countVoxelsOperation(VoxelNode* node, void* extraData); + void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData); VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);