diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 1df82a42a0..4cb6414966 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -822,6 +822,12 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin _radius->setSingleStep(0.01); _radius->setMaximum(FLT_MAX); _radius->setValue(5.0); + + _form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); + _granularity->setMinimum(-FLT_MAX); + _granularity->setMaximum(FLT_MAX); + _granularity->setPrefix("2^"); + _granularity->setValue(8.0); } bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { @@ -851,7 +857,7 @@ bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::Wheel) { float angle = static_cast(event)->angleDelta().y(); const float ANGLE_SCALE = 1.0f / 1000.0f; - _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); + _radius->setValue(_radius->value() * pow(2.0f, angle * ANGLE_SCALE)); return true; } else if (event->type() == QEvent::MouseButtonPress && _positionValid) { @@ -881,7 +887,7 @@ QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) { const int ERASE_MODE_INDEX = 2; return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(), alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX, - _mode->currentIndex() == ERASE_MODE_INDEX)); + _mode->currentIndex() == ERASE_MODE_INDEX, pow(2.0f, _granularity->value()))); } MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) : @@ -956,10 +962,11 @@ QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) { sphere->setScale(_radius->value()); if (alternate) { return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(0, 0, 0, 0), true)); + SharedObjectPointer(), QColor(0, 0, 0, 0), true, false, pow(2.0f, _granularity->value()))); } else { return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - _materialControl->getMaterial(), _materialControl->getColor(), true)); + _materialControl->getMaterial(), _materialControl->getColor(), + true, false, pow(2.0f, _granularity->value()))); } } @@ -974,10 +981,11 @@ QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) { sphere->setScale(_radius->value()); if (alternate) { return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(0, 0, 0, 0))); + SharedObjectPointer(), QColor(0, 0, 0, 0), false, false, pow(2.0f, _granularity->value()))); } else { return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - _materialControl->getMaterial(), _materialControl->getColor())); + _materialControl->getMaterial(), _materialControl->getColor(), + false, false, pow(2.0f, _granularity->value()))); } } @@ -992,13 +1000,14 @@ HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) : QVariant HeightfieldFillBrushTool::createEdit(bool alternate) { const int FILL_MODE_INDEX = 0; if (_mode->currentIndex() == FILL_MODE_INDEX) { - return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value())); + return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value(), + pow(2.0f, _granularity->value()))); } Sphere* sphere = new Sphere(); sphere->setTranslation(_position); sphere->setScale(_radius->value()); return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(), false, true)); + SharedObjectPointer(), QColor(), false, true, pow(2.0f, _granularity->value()))); } HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) : @@ -1017,6 +1026,12 @@ HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) _snapToGrid->setChecked(true); _materialControl = new MaterialControl(this, form, true); + + form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); + _granularity->setMinimum(-FLT_MAX); + _granularity->setMaximum(FLT_MAX); + _granularity->setPrefix("2^"); + _granularity->setValue(8.0); } bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const { @@ -1039,7 +1054,7 @@ void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm: cuboid->setAspectY(vector.y / vector.x); cuboid->setAspectZ(vector.z / vector.x); MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid), - _materialControl->getMaterial(), _materialControl->getColor())) }; + _materialControl->getMaterial(), _materialControl->getColor(), false, false, pow(2.0f, _granularity->value()))) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } @@ -1056,6 +1071,12 @@ HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* _materialControl = new MaterialControl(this, form, true); + form->addRow("Granularity:", _granularity = new QDoubleSpinBox()); + _granularity->setMinimum(-FLT_MAX); + _granularity->setMaximum(FLT_MAX); + _granularity->setPrefix("2^"); + _granularity->setValue(8.0); + QPushButton* place = new QPushButton("Set"); layout()->addWidget(place); connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place); @@ -1076,7 +1097,7 @@ QColor HeightfieldMaterialSpannerTool::getColor() { void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { static_cast(spanner.data())->setWillBeVoxelized(true); MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, - _materialControl->getMaterial(), _materialControl->getColor())) }; + _materialControl->getMaterial(), _materialControl->getColor(), false, false, pow(2.0f, _granularity->value()))) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 23feb940c9..a816b9ebe7 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -326,6 +326,7 @@ protected: QFormLayout* _form; QDoubleSpinBox* _radius; + QDoubleSpinBox* _granularity; glm::vec3 _position; bool _positionValid; @@ -448,6 +449,7 @@ private: QCheckBox* _snapToGrid; MaterialControl* _materialControl; + QDoubleSpinBox* _granularity; }; /// Allows setting heightfield materials by placing a spanner. @@ -470,6 +472,7 @@ private: SharedObjectEditor* _spannerEditor; MaterialControl* _materialControl; + QDoubleSpinBox* _granularity; }; #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index bae5768068..98da28cafa 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -149,12 +149,13 @@ void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects } PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, - float height, bool set, bool erase) : + float height, bool set, bool erase, float granularity) : position(position), radius(radius), height(height), set(set), - erase(erase) { + erase(erase), + granularity(granularity) { } void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -166,7 +167,8 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje Box(position - extents, position + extents), results); foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, height, set, erase); + Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, + height, set, erase, granularity); if (newSpanner != spanner) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); } @@ -179,11 +181,12 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av } HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize) : + const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize, float granularity) : MaterialEdit(material, averageColor), spanner(spanner), paint(paint), - voxelize(voxelize) { + voxelize(voxelize), + granularity(granularity) { } class SpannerProjectionFetchVisitor : public SpannerVisitor { @@ -250,16 +253,18 @@ void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakShared } foreach (const SharedObjectPointer& result, results) { - Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color, paint, voxelize); + Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, + color, paint, voxelize, granularity); if (newResult != result) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult); } } } -FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius) : +FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius, float granularity) : position(position), - radius(radius) { + radius(radius), + granularity(granularity) { } void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -269,7 +274,7 @@ void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjec Box(position - extents, position + extents), results); foreach (const SharedObjectPointer& spanner, results) { - Spanner* newSpanner = static_cast(spanner.data())->fillHeight(position, radius); + Spanner* newSpanner = static_cast(spanner.data())->fillHeight(position, radius, granularity); if (newSpanner != spanner) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 71013996c2..7fbe2b4243 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -205,9 +205,10 @@ public: STREAM float height; STREAM bool set; STREAM bool erase; + STREAM float granularity; PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, - float height = 0.0f, bool set = false, bool erase = false); + float height = 0.0f, bool set = false, bool erase = false, float granularity = 0.0f); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; @@ -237,10 +238,11 @@ public: STREAM SharedObjectPointer spanner; STREAM bool paint; STREAM bool voxelize; + STREAM float granularity; HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), const SharedObjectPointer& material = SharedObjectPointer(), - const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false); + const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false, float granularity = 0.0f); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; @@ -255,8 +257,9 @@ public: STREAM glm::vec3 position; STREAM float radius; + STREAM float granularity; - FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f); + FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float granularity = 0.0f); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index c771b8fb4a..5d9cc24b92 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -111,16 +111,16 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } -Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase) { +Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase, float granularity) { return this; } -Spanner* Spanner::fillHeight(const glm::vec3& position, float radius) { +Spanner* Spanner::fillHeight(const glm::vec3& position, float radius, float granularity) { return this; } Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize) { + const QColor& color, bool paint, bool voxelize, float granularity) { return this; } @@ -1786,7 +1786,7 @@ void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, con HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius, float height, bool set, bool erase, - float normalizeScale, float normalizeOffset) { + float normalizeScale, float normalizeOffset, float granularity) { if (!_height) { return this; } @@ -1813,7 +1813,7 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons HeightfieldNode* newChild = _children[i]->paintHeight(translation + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, position, radius, height, set, erase, normalizeScale, normalizeOffset); + nextScale, position, radius, height, set, erase, normalizeScale, normalizeOffset, granularity); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -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)); @@ -1885,7 +1892,7 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons } HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius) { + const glm::vec3& position, float radius, float granularity) { if (!_height) { return this; } @@ -1911,7 +1918,7 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const HeightfieldNode* newChild = _children[i]->fillHeight(translation + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, position, radius); + nextScale, position, radius, granularity); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -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(); @@ -2088,7 +2101,7 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, - float normalizeScale, float normalizeOffset) { + float normalizeScale, float normalizeOffset, float granularity) { if (!_height) { return this; } @@ -2109,7 +2122,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons HeightfieldNode* newChild = _children[i]->setMaterial(translation + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, nextScale, spanner, - material, color, paint, voxelize, normalizeScale, normalizeOffset); + material, color, paint, voxelize, normalizeScale, normalizeOffset, granularity); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -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() { } @@ -3310,7 +3535,8 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& getScale() * _aspectZ), origin, direction, distance); } -Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase) { +Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height, + bool set, bool erase, float granularity) { // first see if we're going to exceed the range limits float minimumValue = 1.0f, maximumValue = numeric_limits::max(); if (set) { @@ -3328,19 +3554,19 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float Heightfield* newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); newHeightfield->setRoot(HeightfieldNodePointer(_root->paintHeight(newHeightfield->getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), position, radius, height, - set, erase, normalizeScale, normalizeOffset))); + set, erase, normalizeScale, normalizeOffset, granularity))); return newHeightfield; } -Spanner* Heightfield::fillHeight(const glm::vec3& position, float radius) { +Spanner* Heightfield::fillHeight(const glm::vec3& position, float radius, float granularity) { Heightfield* newHeightfield = static_cast(clone(true)); newHeightfield->setRoot(HeightfieldNodePointer(_root->fillHeight(getTranslation(), getRotation(), - glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), position, radius))); + glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), position, radius, granularity))); return newHeightfield; } Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize) { + const QColor& color, bool paint, bool voxelize, float granularity) { // first see if we're going to exceed the range limits, normalizing if necessary Spanner* spannerData = static_cast(spanner.data()); float normalizeScale = 1.0f, normalizeOffset = 0.0f; @@ -3355,7 +3581,7 @@ Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const Shar } newHeightfield->setRoot(HeightfieldNodePointer(_root->setMaterial(newHeightfield->getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), spannerData, - material, color, paint, voxelize, normalizeScale, normalizeOffset))); + material, color, paint, voxelize, normalizeScale, normalizeOffset, granularity))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 653893c84d..b60a104d68 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -77,16 +77,17 @@ public: /// \param set whether to set the height as opposed to raising/lowering it /// \param erase whether to erase height values /// \return the modified spanner, or this if no modification was performed - virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, + bool set, bool erase, float granularity); /// Attempts to fill the spanner's height (adding removing volumetric information). /// \return the modified spanner, or this if no modification was performed - virtual Spanner* fillHeight(const glm::vec3& position, float radius); + virtual Spanner* fillHeight(const glm::vec3& position, float radius, float granularity); /// Attempts to "sculpt" or "paint," etc., with the supplied spanner. /// \return the modified spanner, or this if no modification was performed virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize); + const QColor& color, bool paint, bool voxelize, float granularity); /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -696,17 +697,17 @@ public: HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius, float height, bool set, bool erase, - float normalizeScale, float normalizeOffset); + float normalizeScale, float normalizeOffset, float granularity); HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius); + const glm::vec3& position, float radius, float granularity); void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& editBounds, float& minimum, float& maximum) const; HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, - float normalizeScale, float normalizeOffset); + float normalizeScale, float normalizeOffset, float granularity); void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -736,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; @@ -803,12 +806,13 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, + bool set, bool erase, float granularity); - virtual Spanner* fillHeight(const glm::vec3& position, float radius); + virtual Spanner* fillHeight(const glm::vec3& position, float radius, float granularity); virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint, bool voxelize); + const QColor& color, bool paint, bool voxelize, float granularity); virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 07228c8351..21625dc7d5 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 12; + return 13; default: return 0; }