diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 7c58b7f297..a57cc8a31c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1145,6 +1145,94 @@ HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const H _material(material) { } +const int HEIGHT_LEAF_SIZE = 256 + HeightfieldHeight::HEIGHT_EXTENSION; + +void HeightfieldNode::setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, + const HeightfieldMaterialPointer& material) { + clearChildren(); + + int heightWidth = height->getWidth(); + if (heightWidth <= HEIGHT_LEAF_SIZE) { + _height = height; + _color = color; + _material = material; + return; + } + int heightHeight = height->getContents().size() / heightWidth; + int innerChildHeightWidth = (heightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / 2; + int innerChildHeightHeight = (heightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / 2; + int childHeightWidth = innerChildHeightWidth + HeightfieldHeight::HEIGHT_EXTENSION; + int childHeightHeight = innerChildHeightHeight + HeightfieldHeight::HEIGHT_EXTENSION; + + for (int i = 0; i < CHILD_COUNT; i++) { + QVector childHeightContents(childHeightWidth * childHeightHeight); + quint16* heightDest = childHeightContents.data(); + bool maximumX = (i & X_MAXIMUM_FLAG), maximumY = (i & Y_MAXIMUM_FLAG); + const quint16* heightSrc = height->getContents().constData() + (maximumY ? innerChildHeightHeight * heightWidth : 0) + + (maximumX ? innerChildHeightWidth : 0); + for (int z = 0; z < childHeightHeight; z++, heightDest += childHeightWidth, heightSrc += heightWidth) { + memcpy(heightDest, heightSrc, childHeightWidth * sizeof(quint16)); + } + + HeightfieldColorPointer childColor; + if (color) { + int colorWidth = color->getWidth(); + int colorHeight = color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + int innerChildColorWidth = (colorWidth - HeightfieldData::SHARED_EDGE) / 2; + int innerChildColorHeight = (colorHeight - HeightfieldData::SHARED_EDGE) / 2; + int childColorWidth = innerChildColorWidth + HeightfieldData::SHARED_EDGE; + int childColorHeight = innerChildColorHeight + HeightfieldData::SHARED_EDGE; + QByteArray childColorContents(childColorWidth * childColorHeight * DataBlock::COLOR_BYTES, 0); + char* dest = childColorContents.data(); + const char* src = color->getContents().constData() + ((maximumY ? innerChildColorHeight * colorWidth : 0) + + (maximumX ? innerChildColorWidth : 0)) * DataBlock::COLOR_BYTES; + for (int z = 0; z < childColorHeight; z++, dest += childColorWidth * DataBlock::COLOR_BYTES, + src += colorWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, childColorWidth * DataBlock::COLOR_BYTES); + } + childColor = new HeightfieldColor(childColorWidth, childColorContents); + } + + HeightfieldMaterialPointer childMaterial; + if (material) { + int materialWidth = material->getWidth(); + int materialHeight = material->getContents().size() / materialWidth; + int innerChildMaterialWidth = (materialWidth - HeightfieldData::SHARED_EDGE) / 2; + int innerChildMaterialHeight = (materialHeight - HeightfieldData::SHARED_EDGE) / 2; + int childMaterialWidth = innerChildMaterialWidth + HeightfieldData::SHARED_EDGE; + int childMaterialHeight = innerChildMaterialHeight + HeightfieldData::SHARED_EDGE; + QByteArray childMaterialContents(childMaterialWidth * childMaterialHeight, 0); + QVector childMaterials; + uchar* dest = (uchar*)childMaterialContents.data(); + const uchar* src = (const uchar*)material->getContents().data() + + (maximumY ? innerChildMaterialHeight * materialWidth : 0) + (maximumX ? innerChildMaterialWidth : 0); + QHash materialMap; + for (int z = 0; z < childMaterialHeight; z++, dest += childMaterialWidth, src += materialWidth) { + const uchar* lineSrc = src; + for (uchar* lineDest = dest, *end = dest + childMaterialWidth; lineDest != end; lineDest++, lineSrc++) { + int value = *lineSrc; + if (value != 0) { + int& mapping = materialMap[value]; + if (mapping == 0) { + childMaterials.append(material->getMaterials().at(value - 1)); + mapping = childMaterials.size(); + } + value = mapping; + } + *lineDest = value; + } + } + childMaterial = new HeightfieldMaterial(childMaterialWidth, childMaterialContents, childMaterials); + } + + _children[i] = new HeightfieldNode(); + _children[i]->setContents(HeightfieldHeightPointer(new HeightfieldHeight(childHeightWidth, childHeightContents)), + childColor, childMaterial); + } + + mergeChildren(); +} + bool HeightfieldNode::isLeaf() const { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { @@ -1406,8 +1494,7 @@ void HeightfieldNode::mergeChildren() { Heightfield::Heightfield() : _aspectY(1.0f), - _aspectZ(1.0f), - _root(new HeightfieldNode()) { + _aspectZ(1.0f) { connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds); connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds); @@ -1415,6 +1502,11 @@ Heightfield::Heightfield() : connect(this, &Heightfield::aspectYChanged, this, &Heightfield::updateBounds); connect(this, &Heightfield::aspectZChanged, this, &Heightfield::updateBounds); updateBounds(); + + connect(this, &Heightfield::heightChanged, this, &Heightfield::updateRoot); + connect(this, &Heightfield::colorChanged, this, &Heightfield::updateRoot); + connect(this, &Heightfield::materialChanged, this, &Heightfield::updateRoot); + updateRoot(); } void Heightfield::setAspectY(float aspectY) { @@ -2486,6 +2578,13 @@ void Heightfield::updateBounds() { setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(glm::vec3(), extent)); } +void Heightfield::updateRoot() { + _root = new HeightfieldNode(); + if (_height) { + _root->setContents(_height, _color, _material); + } +} + MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { // after transforming into unit space, we scale the threshold in proportion to vertical distance glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index bb79a6fc51..d67c23909d 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -494,6 +494,9 @@ public: const HeightfieldColorPointer& color = HeightfieldColorPointer(), const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer()); + void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, + const HeightfieldMaterialPointer& material); + const HeightfieldHeightPointer& getHeight() const { return _height; } const HeightfieldColorPointer& getColor() const { return _color; } const HeightfieldMaterialPointer& getMaterial() const { return _material; } @@ -533,9 +536,10 @@ class Heightfield : public Transformable { Q_OBJECT Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) - Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged) - Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged DESIGNABLE false) + Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged STORED false) + Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false) + Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false + DESIGNABLE false) public: @@ -600,6 +604,7 @@ protected: private slots: void updateBounds(); + void updateRoot(); private: