From a0f75c990e73eb38c36090560852c76aa97aeb83 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Nov 2014 17:12:47 -0800 Subject: [PATCH] Working on fetching/clearing heightfield chunks. --- interface/src/MetavoxelSystem.cpp | 11 + libraries/metavoxels/src/MetavoxelData.cpp | 4 + .../metavoxels/src/MetavoxelMessages.cpp | 292 ++---------------- libraries/metavoxels/src/Spanner.cpp | 169 +++++++++- libraries/metavoxels/src/Spanner.h | 7 + 5 files changed, 214 insertions(+), 269 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 28c2707049..db3ab28b8f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -363,6 +363,17 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().bind(); + + SpannerCursorRenderVisitor spannerVisitor(bounds); + guide(spannerVisitor); + + DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release(); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_POLYGON_OFFSET_FILL); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 94b3e0be97..535a336da7 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -344,6 +344,10 @@ int SpannerReplaceVisitor::visit(MetavoxelInfo& info) { void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) { Spanner* newSpanner = static_cast(newObject.data()); + if (!newSpanner) { + remove(attribute, bounds, granularity, oldObject); + return; + } if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) { // if the bounds have changed, we must remove and reinsert remove(attribute, bounds, granularity, oldObject); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index dc563d5c0e..a4d2569de0 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -155,7 +155,9 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position } void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - glm::vec3 extents(radius, radius, radius); + // increase the extents slightly to include neighboring tiles + const float RADIUS_EXTENSION = 1.1f; + glm::vec3 extents = glm::vec3(radius, radius, radius) * RADIUS_EXTENSION; QVector results; data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), Box(position - extents, position + extents), results); @@ -492,262 +494,6 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } -class HeightfieldClearFetchVisitor : public MetavoxelVisitor { -public: - - HeightfieldClearFetchVisitor(const Box& bounds, float granularity); - - const SharedObjectPointer& getSpanner() const { return _spanner; } - - virtual int visit(MetavoxelInfo& info); - -private: - - Box _bounds; - Box _expandedBounds; - SharedObjectPointer _spanner; - Box _spannerBounds; - int _heightfieldWidth; - int _heightfieldHeight; -}; - -HeightfieldClearFetchVisitor::HeightfieldClearFetchVisitor(const Box& bounds, float granularity) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()) { - - // find the bounds of all voxel nodes intersected - float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(glm::log(granularity) / glm::log(2.0f))); - _bounds.minimum = glm::floor(bounds.minimum / nodeSize) * nodeSize; - _bounds.maximum = glm::ceil(bounds.maximum / nodeSize) * nodeSize; - - // expand to include edges - _expandedBounds = _bounds; - float increment = nodeSize / VOXEL_BLOCK_SIZE; - _expandedBounds.maximum.x += increment; - _expandedBounds.maximum.z += increment; -} - -int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - if (!bounds.intersects(_expandedBounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer heightPointer = info.inputValues.at(0).getInlineValue(); - if (!heightPointer) { - return STOP_RECURSION; - } - QByteArray contents(heightPointer->getContents()); - int size = glm::sqrt((float)contents.size()); - float heightScale = size / info.size; - - Box overlap = bounds.getIntersection(_expandedBounds); - int srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - int srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - int srcWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - int srcHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* src = contents.data() + srcY * size + srcX; - - // check for non-zero values - bool foundNonZero = false; - for (int y = 0; y < srcHeight; y++, src += (size - srcWidth)) { - for (char* end = src + srcWidth; src != end; src++) { - if (*src != 0) { - foundNonZero = true; - goto outerBreak; - } - } - } - outerBreak: - - // if everything is zero, we're done - if (!foundNonZero) { - return STOP_RECURSION; - } - - // create spanner if necessary - TempHeightfield* spanner = static_cast(_spanner.data()); - float increment = 1.0f / heightScale; - if (!spanner) { - _spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment; - _spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment; - _spannerBounds.minimum.y = bounds.minimum.y; - _spannerBounds.maximum.y = bounds.maximum.y; - _heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment); - _heightfieldHeight = (int)glm::round((_spannerBounds.maximum.z - _spannerBounds.minimum.z) / increment); - int heightfieldArea = _heightfieldWidth * _heightfieldHeight; - Box innerBounds = _spannerBounds; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - _spanner = spanner = new TempHeightfield(innerBounds, increment, QByteArray(heightfieldArea, 0), - QByteArray(heightfieldArea * DataBlock::COLOR_BYTES, 0), QByteArray(heightfieldArea, 0), - QVector()); - } - - // copy the inner area - overlap = bounds.getIntersection(_spannerBounds); - int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + srcY * size + srcX; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - memcpy(dest, src, destWidth); - } - - // clear the inner area - Box innerBounds = _spannerBounds; - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - Box innerOverlap = bounds.getIntersection(innerBounds); - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - dest = contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - // see if there are any non-zero values left - foundNonZero = false; - dest = contents.data(); - for (char* end = dest + contents.size(); dest != end; dest++) { - if (*dest != 0) { - foundNonZero = true; - break; - } - } - - // if all is gone, clear the node - if (foundNonZero) { - HeightfieldHeightDataPointer newHeightPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newHeightPointer)); - - } else { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - } - - // allow a border for what we clear in terms of color/material - innerBounds.minimum.x += increment; - innerBounds.minimum.z += increment; - innerBounds.maximum.x -= increment; - innerBounds.maximum.z -= increment; - innerOverlap = bounds.getIntersection(innerBounds); - - HeightfieldColorDataPointer colorPointer = info.inputValues.at(1).getInlineValue(); - if (colorPointer) { - contents = colorPointer->getContents(); - size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES, - src += size * DataBlock::COLOR_BYTES) { - memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES); - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES; - - for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) { - memset(dest, 0, destWidth * DataBlock::COLOR_BYTES); - } - - HeightfieldColorDataPointer newColorPointer(new HeightfieldColorData(contents)); - info.outputValues[1] = AttributeValue(_outputs.at(1), - encodeInline(newColorPointer)); - } - } else { - info.outputValues[1] = AttributeValue(_outputs.at(1)); - } - } - - HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); - if (materialPointer) { - contents = materialPointer->getContents(); - QVector materials = materialPointer->getMaterials(); - size = glm::sqrt((float)contents.size()); - heightScale = size / info.size; - - // copy the inner area - destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; - destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); - uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX; - srcX = (overlap.minimum.x - info.minimum.x) * heightScale; - srcY = (overlap.minimum.z - info.minimum.z) * heightScale; - uchar* src = (uchar*)contents.data() + srcY * size + srcX; - QHash materialMap; - - for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { - for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) { - int material = *lineSrc; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(), - spanner->getMaterial()); - } - material = mapping; - } - *lineDest = material; - } - } - - if (foundNonZero) { - destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; - destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); - destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); - if (destWidth > 0 && destHeight > 0) { - dest = (uchar*)contents.data() + destY * size + destX; - - for (int y = 0; y < destHeight; y++, dest += size) { - memset(dest, 0, destWidth); - } - - clearUnusedMaterials(materials, contents); - HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[2] = AttributeValue(_outputs.at(2), - encodeInline(newMaterialPointer)); - } - } else { - info.outputValues[2] = AttributeValue(_outputs.at(2)); - } - } - - return STOP_RECURSION; -} - void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit Spanner* spanner = static_cast(this->spanner.data()); @@ -758,14 +504,34 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject QColor color = averageColor; color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - // clear/fetch any heightfield data - HeightfieldClearFetchVisitor heightfieldVisitor(spanner->getBounds(), spanner->getVoxelizationGranularity()); - data.guide(heightfieldVisitor); + // find the bounds of all voxel nodes intersected + float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor( + glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f))); + Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize, + glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize); + + // expand to include edges + Box expandedBounds = bounds; + float increment = nodeSize / VOXEL_BLOCK_SIZE; + expandedBounds.maximum.x += increment; + expandedBounds.maximum.z += increment; + + // get all intersecting spanners + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results); + + // clear/voxelize as appropriate + SharedObjectPointer heightfield; + foreach (const SharedObjectPointer& result, results) { + Spanner* newSpanner = static_cast(result.data())->clearAndFetchHeight(bounds, heightfield); + if (newSpanner != result) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner); + } + } // voxelize the fetched heightfield, if any - if (heightfieldVisitor.getSpanner()) { - VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfieldVisitor.getSpanner().data()), - material, color); + if (heightfield) { + VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfield.data()), material, color); data.guide(visitor); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 35b0754727..3cd2b94e1b 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -117,6 +117,10 @@ Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float hei return this; } +Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + return this; +} + bool Spanner::hasOwnColors() const { return false; } @@ -1814,6 +1818,159 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float return newHeightfield; } +Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + float heightIncrementX = getScale() / innerHeightWidth; + float heightIncrementZ = (getScale() * _aspectZ) / innerHeightHeight; + + int colorWidth = heightWidth; + int colorHeight = heightHeight; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + float colorIncrementX = getScale() / innerColorWidth; + float colorIncrementZ = (getScale() * _aspectZ) / innerColorHeight; + + int materialWidth = colorWidth; + int materialHeight = colorHeight; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + float materialIncrementX = getScale() / innerMaterialWidth; + float materialIncrementZ = (getScale() * _aspectZ) / innerMaterialHeight; + + float largestIncrementX = qMax(heightIncrementX, qMax(colorIncrementX, materialIncrementX)); + float largestIncrementZ = qMax(heightIncrementZ, qMax(colorIncrementZ, materialIncrementZ)); + + glm::vec3 minimum(glm::floor(bounds.minimum.x / largestIncrementX) * largestIncrementX, getBounds().minimum.y, + glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ); + glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, getBounds().maximum.y, + glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); + Box largestBounds(minimum, maximum); + + glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(getRotation())) * glm::translate(-getTranslation()); + glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ)); + glm::mat4 transform = glm::scale(inverseScale) * baseTransform; + Box transformedBounds = transform * largestBounds; + + // make sure there are values to clear + int startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightWidth - 1); + int startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + int endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, 0, heightWidth - 1); + int endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, + 0, heightHeight - 1); + const quint16* src = _height->getContents().constData() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = startX; x <= endX; x++) { + if (*lineSrc++ != 0) { + goto clearableBreak; + } + } + } + return this; + clearableBreak: + + // create heightfield if necessary + Heightfield* spanner = static_cast(heightfield.data()); + if (!spanner) { + int spannerHeightWidth = (int)((maximum.x - minimum.x) / heightIncrementX) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerHeightHeight = (int)((maximum.z - minimum.z) / heightIncrementZ) + HeightfieldHeight::HEIGHT_EXTENSION; + int spannerColorWidth = (int)((maximum.x - bounds.minimum.x) / colorIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerColorHeight = (int)((maximum.z - minimum.z) / colorIncrementZ) + HeightfieldData::SHARED_EDGE; + int spannerMaterialWidth = (int)((maximum.x - minimum.x) / materialIncrementX) + HeightfieldData::SHARED_EDGE; + int spannerMaterialHeight = (int)((maximum.z - minimum.z) / materialIncrementZ) + HeightfieldData::SHARED_EDGE; + + heightfield = spanner = new Heightfield(); + spanner->setTranslation(minimum); + spanner->setScale(maximum.x - minimum.x); + spanner->setAspectY((maximum.y - minimum.y) / spanner->getScale()); + spanner->setAspectZ((maximum.z - minimum.z) / spanner->getScale()); + spanner->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(spannerHeightWidth, + QVector(spannerHeightWidth * spannerHeightHeight)))); + spanner->setColor(HeightfieldColorPointer(new HeightfieldColor(spannerColorWidth, + QByteArray(spannerColorWidth * spannerColorHeight * DataBlock::COLOR_BYTES, 0xFF)))); + spanner->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(spannerMaterialWidth, + QByteArray(spannerMaterialWidth * spannerMaterialHeight, 0), QVector()))); + } + + // clear the height + QVector newHeightContents = _height->getContents(); + quint16* dest = newHeightContents.data() + startZ * heightWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += heightWidth) { + memset(dest, 0, (endX - startX + 1) * sizeof(quint16)); + } + + // if we've cleared all the inner height, we can remove the spanner entirely + src = newHeightContents.constData() + heightWidth + HeightfieldHeight::HEIGHT_BORDER; + for (int z = 0; z < innerHeightHeight; z++, src += heightWidth) { + const quint16* lineSrc = src; + for (int x = 0; x < innerHeightWidth; x++) { + if (*lineSrc++ != 0) { + goto nonEmptyBreak; + } + } + } + return NULL; + nonEmptyBreak: + + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents))); + + // and the color + if (_color) { + inverseScale = glm::vec3(innerColorWidth / getScale(), 1.0f, innerColorHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, colorWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, colorHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, colorWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, colorHeight - 1); + QByteArray newColorContents = _color->getContents(); + char* dest = newColorContents.data() + (startZ * colorWidth + startX) * DataBlock::COLOR_BYTES; + for (int z = startZ; z <= endZ; z++, dest += colorWidth * DataBlock::COLOR_BYTES) { + memset(dest, 0, (endX - startX + 1) * DataBlock::COLOR_BYTES); + } + newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents))); + } + + // and the material + if (_material) { + inverseScale = glm::vec3(innerMaterialWidth / getScale(), 1.0f, innerMaterialHeight / (getScale() * _aspectZ)); + transform = glm::scale(inverseScale) * baseTransform; + transformedBounds = transform * largestBounds; + startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, materialWidth - 1); + startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, materialHeight - 1); + endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, materialWidth - 1); + endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, materialHeight - 1); + QByteArray newMaterialContents = _material->getContents(); + QVector newMaterials = _material->getMaterials(); + char* dest = newMaterialContents.data() + startZ * materialWidth + startX; + for (int z = startZ; z <= endZ; z++, dest += materialWidth) { + memset(dest, 0, endX - startX + 1); + } + clearUnusedMaterials(newMaterials, newMaterialContents); + newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial( + materialWidth, newMaterialContents, newMaterials))); + } + + return newHeightfield; +} + bool Heightfield::hasOwnColors() const { return _color; } @@ -1953,32 +2110,32 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float const float DISTANCE_THRESHOLD = 0.001f; if (glm::abs(entry.x - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(-1.0f, 0.0f, 0.0f); + normal = getRotation() * glm::vec3(-1.0f, 0.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.x - innerWidth) < DISTANCE_THRESHOLD) { - normal = glm::vec3(1.0f, 0.0f, 0.0f); + normal = getRotation() * glm::vec3(1.0f, 0.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.y - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, -1.0f, 0.0f); + normal = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.y - numeric_limits::max()) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 1.0f, 0.0f); + normal = getRotation() * glm::vec3(0.0f, 1.0f, 0.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.z - 0.0f) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, -1.0f); + normal = getRotation() * glm::vec3(0.0f, 0.0f, -1.0f); distance = boundsDistance; return true; } else if (glm::abs(entry.z - innerHeight) < DISTANCE_THRESHOLD) { - normal = glm::vec3(0.0f, 0.0f, 1.0f); + normal = getRotation() * glm::vec3(0.0f, 0.0f, 1.0f); distance = boundsDistance; return true; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 094524ab3e..45544f5377 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -73,6 +73,11 @@ public: /// \return the modified spanner, or this if no modification was performed virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + /// Attempts to clear and fetch part of the spanner's height. + /// \param heightfield the heightfield to populate + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -525,6 +530,8 @@ public: virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; virtual QRgb getColorAt(const glm::vec3& point);