diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 05d765f9d4..5d9cc24b92 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1846,6 +1846,13 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } + // if the granularity is insufficient, we must subdivide + if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { + HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); + return newNode->paintHeight(translation, rotation, scale, position, radius, height, set, + erase, 1.0f, 0.0f, granularity); + } + // now apply the actual change glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); @@ -1927,9 +1934,15 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const if (!_stack) { return this; } - QVector newHeightContents = _height->getContents(); + // if the granularity is insufficient, we must subdivide + QVector newHeightContents = _height->getContents(); QVector newStackContents = _stack->getContents(); + if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { + HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); + return newNode->fillHeight(translation, rotation, scale, position, radius, granularity); + } + int stackWidth = _stack->getWidth(); int stackHeight = newStackContents.size() / stackWidth; QVector newStackMaterials = _stack->getMaterials(); @@ -2149,6 +2162,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } + + // if the granularity is insufficient, we must subdivide + if (scale.x / innerHeightWidth > granularity || scale.z / innerHeightHeight > granularity) { + HeightfieldNodePointer newNode(subdivide(newHeightContents, newStackContents)); + return newNode->setMaterial(translation, rotation, scale, spanner, material, color, + paint, voxelize, 1.0f, 0.0f, granularity); + } + QVector oldHeightContents = newHeightContents; QVector oldStackContents = newStackContents; @@ -3210,6 +3231,210 @@ bool HeightfieldNode::findHeightfieldRayIntersection(const glm::vec3& origin, co return false; } +static inline float mixHeights(float firstHeight, float secondHeight, float t) { + return (firstHeight == 0.0f) ? secondHeight : (secondHeight == 0.0f ? firstHeight : + glm::mix(firstHeight, secondHeight, t)); +} + +HeightfieldNode* HeightfieldNode::subdivide(const QVector& heightContents, + const QVector& stackContents) const { + HeightfieldNode* newNode = new HeightfieldNode(*this); + int heightWidth = _height->getWidth(); + int heightHeight = heightContents.size() / heightWidth; + newNode->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, heightContents))); + int stackWidth = 0, stackHeight = 0; + QVector stackMaterials; + if (_stack) { + stackWidth = _stack->getWidth(); + stackHeight = stackContents.size() / stackWidth; + stackMaterials = _stack->getMaterials(); + newNode->setStack(HeightfieldStackPointer(new HeightfieldStack(stackWidth, stackContents, stackMaterials))); + } + int colorWidth = 0, colorHeight = 0; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + } + int materialWidth = 0, materialHeight = 0; + QVector materialMaterials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + materialMaterials = _material->getMaterials(); + } + for (int i = 0; i < CHILD_COUNT; i++) { + QVector childHeightContents(heightWidth * heightHeight); + QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF); + QByteArray childMaterialContents(materialWidth * materialHeight, 0); + QVector childStackContents(stackWidth * stackHeight); + + quint16* heightDest = childHeightContents.data(); + const quint16* heightSrc = heightContents.constData() + (i & Y_MAXIMUM_FLAG ? (heightHeight / 2) * heightWidth : 0) + + (i & X_MAXIMUM_FLAG ? heightWidth / 2 : 0); + for (int z = 0; z < heightHeight; z++) { + float srcZ = z * 0.5f + 0.5f; + float fractZ = glm::fract(srcZ); + const quint16* heightSrcZ = heightSrc + (int)srcZ * heightWidth; + for (int x = 0; x < heightWidth; x++) { + float srcX = x * 0.5f + 0.5f; + float fractX = glm::fract(srcX); + const quint16* heightSrcX = heightSrcZ + (int)srcX; + if (fractZ == 0.0f) { + if (fractX == 0.0f) { + *heightDest++ = heightSrcX[0]; + } else { + *heightDest++ = mixHeights(heightSrcX[0], heightSrcX[1], fractX); + } + } else { + if (fractX == 0.0f) { + *heightDest++ = mixHeights(heightSrcX[0], heightSrcX[heightWidth], fractZ); + } else { + *heightDest++ = mixHeights(mixHeights(heightSrcX[0], heightSrcX[1], fractX), + mixHeights(heightSrcX[heightWidth], heightSrcX[heightWidth + 1], fractX), fractZ); + } + } + } + } + + if (colorWidth != 0) { + char* colorDest = childColorContents.data(); + const uchar* colorSrc = (const uchar*)_color->getContents().constData() + + ((i & Y_MAXIMUM_FLAG ? (colorHeight / 2) * colorWidth : 0) + + (i & X_MAXIMUM_FLAG ? colorWidth / 2 : 0)) * DataBlock::COLOR_BYTES; + for (int z = 0; z < colorHeight; z++) { + float srcZ = z * 0.5f; + float fractZ = glm::fract(srcZ); + const uchar* colorSrcZ = colorSrc + (int)srcZ * colorWidth * DataBlock::COLOR_BYTES; + for (int x = 0; x < colorWidth; x++) { + float srcX = x * 0.5f; + float fractX = glm::fract(srcX); + const uchar* colorSrcX = colorSrcZ + (int)srcX * DataBlock::COLOR_BYTES; + const uchar* nextColorSrcX = colorSrcX + colorWidth * DataBlock::COLOR_BYTES; + if (fractZ == 0.0f) { + if (fractX == 0.0f) { + *colorDest++ = colorSrcX[0]; + *colorDest++ = colorSrcX[1]; + *colorDest++ = colorSrcX[2]; + } else { + *colorDest++ = glm::mix(colorSrcX[0], colorSrcX[3], fractX); + *colorDest++ = glm::mix(colorSrcX[1], colorSrcX[4], fractX); + *colorDest++ = glm::mix(colorSrcX[2], colorSrcX[5], fractX); + } + } else { + if (fractX == 0.0f) { + *colorDest++ = glm::mix(colorSrcX[0], nextColorSrcX[0], fractZ); + *colorDest++ = glm::mix(colorSrcX[1], nextColorSrcX[1], fractZ); + *colorDest++ = glm::mix(colorSrcX[2], nextColorSrcX[2], fractZ); + } else { + *colorDest++ = glm::mix(glm::mix(colorSrcX[0], colorSrcX[3], fractX), + glm::mix(nextColorSrcX[0], nextColorSrcX[3], fractX), fractZ); + *colorDest++ = glm::mix(glm::mix(colorSrcX[1], colorSrcX[4], fractX), + glm::mix(nextColorSrcX[1], nextColorSrcX[4], fractX), fractZ); + *colorDest++ = glm::mix(glm::mix(colorSrcX[2], colorSrcX[5], fractX), + glm::mix(nextColorSrcX[2], nextColorSrcX[5], fractX), fractZ); + } + } + } + } + } + + if (materialWidth != 0) { + char* materialDest = childMaterialContents.data(); + const char* materialSrc = _material->getContents().constData() + + (i & Y_MAXIMUM_FLAG ? (materialHeight / 2) * materialWidth : 0) + + (i & X_MAXIMUM_FLAG ? materialWidth / 2 : 0); + for (int z = 0; z < materialHeight; z++) { + float srcZ = z * 0.5f; + const char* materialSrcZ = materialSrc + (int)srcZ * materialWidth; + for (int x = 0; x < materialWidth; x++) { + float srcX = x * 0.5f; + const char* materialSrcX = materialSrcZ + (int)srcX; + *materialDest++ = *materialSrcX; + } + } + } + + if (stackWidth != 0) { + StackArray* stackDest = childStackContents.data(); + const StackArray* stackSrc = _stack->getContents().constData() + + (i & Y_MAXIMUM_FLAG ? (stackHeight / 2) * stackWidth : 0) + + (i & X_MAXIMUM_FLAG ? stackWidth / 2 : 0); + for (int z = 0; z < stackHeight; z++) { + float srcZ = z * 0.5f; + float fractZ = glm::fract(srcZ); + const StackArray* stackSrcZ = stackSrc + (int)srcZ * stackWidth; + for (int x = 0; x < stackWidth; x++) { + float srcX = x * 0.5f; + float fractX = glm::fract(srcX); + const StackArray* stackSrcX = stackSrcZ + (int)srcX; + if (stackSrcX->isEmpty()) { + stackDest++; + continue; + } + int minimumY = stackSrcX->getPosition() * 2; + int maximumY = (stackSrcX->getPosition() + stackSrcX->getEntryCount() - 1) * 2; + *stackDest = StackArray(maximumY - minimumY + 1); + stackDest->setPosition(minimumY); + for (int y = minimumY; y <= maximumY; y++) { + float srcY = y * 0.5f; + float fractY = glm::fract(srcY); + const StackArray::Entry& srcEntry = stackSrcX->getEntry((int)srcY); + StackArray::Entry& destEntry = stackDest->getEntry(y); + destEntry.color = srcEntry.color; + destEntry.material = srcEntry.material; + if (srcEntry.hermiteX != 0) { + glm::vec3 normal; + float distance = srcEntry.getHermiteX(normal); + if (distance < fractX) { + const StackArray::Entry& nextSrcEntryX = stackSrcX[1].getEntry((int)srcY); + destEntry.color = nextSrcEntryX.color; + destEntry.material = nextSrcEntryX.material; + + } else { + destEntry.setHermiteX(normal, (distance - fractX) / 0.5f); + } + } + if (srcEntry.hermiteY != 0) { + glm::vec3 normal; + float distance = srcEntry.getHermiteY(normal); + if (distance < fractY) { + const StackArray::Entry& nextSrcEntryY = stackSrcX->getEntry((int)srcY + 1); + destEntry.color = nextSrcEntryY.color; + destEntry.material = nextSrcEntryY.material; + + } else { + destEntry.setHermiteY(normal, (distance - fractY) / 0.5f); + } + } + if (srcEntry.hermiteZ != 0) { + glm::vec3 normal; + float distance = srcEntry.getHermiteZ(normal); + if (distance < fractZ) { + const StackArray::Entry& nextSrcEntryZ = stackSrcX[stackWidth].getEntry((int)srcY); + destEntry.color = nextSrcEntryZ.color; + destEntry.material = nextSrcEntryZ.material; + + } else { + destEntry.setHermiteZ(normal, (distance - fractZ) / 0.5f); + } + } + } + stackDest++; + } + } + } + + newNode->setChild(i, HeightfieldNodePointer(new HeightfieldNode( + HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, childHeightContents)), + HeightfieldColorPointer(colorWidth == 0 ? NULL : new HeightfieldColor(colorWidth, childColorContents)), + HeightfieldMaterialPointer(materialWidth == 0 ? NULL : + new HeightfieldMaterial(materialWidth, childMaterialContents, materialMaterials)), + HeightfieldStackPointer(stackWidth == 0 ? NULL : + new HeightfieldStack(stackWidth, childStackContents, stackMaterials))))); + } + return newNode; +} + AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() { } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 42aad203c6..b60a104d68 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -737,6 +737,8 @@ private: bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; + HeightfieldNode* subdivide(const QVector& heightContents, const QVector& stackContents) const; + HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material;