From f192f2428eb285d009ef4c7d75b1ac0b0b0a8e2b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Nov 2014 18:06:38 -0800 Subject: [PATCH] Height painting. --- .../metavoxels/src/MetavoxelMessages.cpp | 96 +++---------------- libraries/metavoxels/src/Spanner.cpp | 63 +++++++++++- libraries/metavoxels/src/Spanner.h | 12 ++- 3 files changed, 85 insertions(+), 86 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index c7471e7296..dc563d5c0e 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -154,87 +154,18 @@ PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position height(height) { } -class PaintHeightfieldHeightEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - PaintHeightfieldHeightEdit _edit; - Box _bounds; -}; - -PaintHeightfieldHeightEditVisitor::PaintHeightfieldHeightEditVisitor(const PaintHeightfieldHeightEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute(), - QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute()), - _edit(edit) { - - glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); - _bounds = Box(_edit.position - extents, _edit.position + extents); -} - -const int EIGHT_BIT_MAXIMUM = 255; - -int PaintHeightfieldHeightEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldHeightDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - QByteArray contents(pointer->getContents()); - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (_edit.position - info.minimum) * heightScale; - float scaledRadius = _edit.radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // raise/lower all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float scaledHeight = _edit.height * EIGHT_BIT_MAXIMUM / info.size; - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - if (value != *dest) { - *dest = qMin(qMax(value, 0), EIGHT_BIT_MAXIMUM); - changed = true; - } - } - } - lineDest += size; - } - if (changed) { - HeightfieldHeightDataPointer newPointer(new HeightfieldHeightData(contents)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); - } - return STOP_RECURSION; -} - void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldHeightEditVisitor visitor(*this); - data.guide(visitor); + glm::vec3 extents(radius, radius, radius); + QVector results; + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + Box(position - extents, position + extents), results); + + foreach (const SharedObjectPointer& spanner, results) { + Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, height); + if (newSpanner != spanner) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); + } + } } MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& averageColor) : @@ -256,7 +187,7 @@ void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedOb Box(position - extents, position + extents), results); foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->paint(position, radius, material, averageColor); + Spanner* newSpanner = static_cast(spanner.data())->paintMaterial(position, radius, material, averageColor); if (newSpanner != spanner) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); } @@ -419,7 +350,8 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { if (minZ > 0) { hermiteMinZ--; hermiteSizeZ++; - } + } + const int EIGHT_BIT_MAXIMUM = 255; QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples + hermiteMinX * VoxelHermiteData::EDGE_COUNT; for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) { diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 0808ae2ed1..325bd80d04 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -108,7 +108,12 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } -Spanner* Spanner::paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { +Spanner* Spanner::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color) { + return this; +} + +Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height) { return this; } @@ -1608,7 +1613,7 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& return false; } -Spanner* Heightfield::paint(const glm::vec3& position, float radius, +Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { if (!_height) { return this; @@ -1714,6 +1719,60 @@ Spanner* Heightfield::paint(const glm::vec3& position, float radius, return newHeightfield; } +Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { + if (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + QVector contents = _height->getContents(); + int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = heightWidth - 1; + int highestZ = heightHeight - 1; + Heightfield* newHeightfield = static_cast(clone(true)); + + glm::vec3 inverseScale(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); + glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; + center.x += 1.0f; + center.z += 1.0f; + + glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; + glm::vec3 start = glm::floor(center - extents); + glm::vec3 end = glm::ceil(center + extents); + + // paint all points within the radius + float z = qMax(start.z, 0.0f); + float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); + quint16* lineDest = contents.data() + (int)z * heightWidth + (int)startX; + float squaredRadius = extents.x * extents.x; + float squaredRadiusReciprocal = 1.0f / squaredRadius; + float scaledHeight = height * numeric_limits::max() / (getScale() * _aspectY); + float multiplierZ = inverseScale.x / inverseScale.z; + bool changed = false; + for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + quint16* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + // height falls off towards edges + int value = *dest + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + if (value != *dest) { + *dest = qMin(qMax(value, 0), (int)numeric_limits::max()); + changed = true; + } + } + } + lineDest += heightWidth; + } + if (changed) { + newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); + } + + return newHeightfield; +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index beaf6c8a7e..6e4e273726 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -66,7 +66,12 @@ public: /// Attempts to paint on the spanner. /// \return the modified spanner, or this if no modification was performed - virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + + /// Attempts to modify the spanner's height. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -515,8 +520,11 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color); + virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, + const QColor& color); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + signals: void aspectYChanged(float aspectY);