From 08373f8b1ebc6fbd7777375ffbf7d8d3028e2a51 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Dec 2014 15:02:35 -0800 Subject: [PATCH 01/67] Fix for message upload stats. --- .../metavoxels/src/DatagramSequencer.cpp | 24 ++++++++++++------- libraries/metavoxels/src/DatagramSequencer.h | 8 ++++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 1aeef8e450..94b92910ee 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -707,8 +707,9 @@ void ReliableChannel::endMessage() { quint32 length = _buffer.pos() - _messageLengthPlaceholder; _buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length); - _messageReceivedOffset = getBytesWritten(); - _messageSize = length; + + pruneOutgoingMessageStats(); + _outgoingMessageStats.append(OffsetSizePair(getBytesWritten(), length)); } void ReliableChannel::sendMessage(const QVariant& message) { @@ -717,12 +718,14 @@ void ReliableChannel::sendMessage(const QVariant& message) { endMessage(); } -bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const { - if (!_messagesEnabled || _offset >= _messageReceivedOffset) { +bool ReliableChannel::getMessageSendProgress(int& sent, int& total) { + pruneOutgoingMessageStats(); + if (!_messagesEnabled || _outgoingMessageStats.isEmpty()) { return false; } - sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset)); - total = _messageSize; + const OffsetSizePair& stat = _outgoingMessageStats.first(); + sent = qMax(0, stat.second - (stat.first - _offset)); + total = stat.second; return true; } @@ -762,8 +765,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _offset(0), _writePosition(0), _writePositionResetPacketNumber(0), - _messagesEnabled(true), - _messageReceivedOffset(0) { + _messagesEnabled(true) { _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); @@ -934,3 +936,9 @@ void ReliableChannel::readData(QDataStream& in) { } } +void ReliableChannel::pruneOutgoingMessageStats() { + while (!_outgoingMessageStats.isEmpty() && _offset >= _outgoingMessageStats.first().first) { + _outgoingMessageStats.removeFirst(); + } +} + diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 02fbd0f365..7f780d7b4d 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -384,7 +384,7 @@ public: /// Determines the number of bytes uploaded towards the currently pending message. /// \return true if there is a message pending, in which case the sent and total arguments will be set - bool getMessageSendProgress(int& sent, int& total) const; + bool getMessageSendProgress(int& sent, int& total); /// Determines the number of bytes downloaded towards the currently pending message. /// \return true if there is a message pending, in which case the received and total arguments will be set @@ -416,6 +416,8 @@ private: void readData(QDataStream& in); + void pruneOutgoingMessageStats(); + int _index; bool _output; CircularBuffer _buffer; @@ -430,6 +432,10 @@ private: SpanList _acknowledged; bool _messagesEnabled; int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message + + typedef QPair OffsetSizePair; + QVector _outgoingMessageStats; + int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set }; From c96be3fe3d9dfd3ff0834fdcd1adb1aecc0aa5bf Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Dec 2014 17:46:28 -0800 Subject: [PATCH 02/67] Removed unused functions, stubbing out "stack" layer. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- libraries/metavoxels/src/MetavoxelData.cpp | 61 --------- libraries/metavoxels/src/MetavoxelData.h | 4 +- libraries/metavoxels/src/Spanner.cpp | 121 +++++++++++++++--- libraries/metavoxels/src/Spanner.h | 53 +++++++- libraries/networking/src/PacketHeaders.cpp | 2 +- 6 files changed, 151 insertions(+), 92 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 89b3102391..912183f5d6 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : const char* SAVE_FILE = "/resources/metavoxels.dat"; const int FILE_MAGIC = 0xDADAFACE; -const int FILE_VERSION = 2; +const int FILE_VERSION = 3; void MetavoxelPersister::load() { QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 860ab3e5e9..a412659f42 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1188,67 +1188,6 @@ void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const { } } -void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const { - SharedObjectSet oldSet = decodeInline(reference.getAttributeValue()); - SharedObjectSet newSet = decodeInline(_attributeValue); - foreach (const SharedObjectPointer& object, oldSet) { - if (static_cast(object.data())->testAndSetVisited(state.base.visit) && !newSet.contains(object)) { - state.base.stream << object; - } - } - foreach (const SharedObjectPointer& object, newSet) { - if (static_cast(object.data())->testAndSetVisited(state.base.visit) && !oldSet.contains(object)) { - state.base.stream << object; - } - } - if (isLeaf() || !state.shouldSubdivide()) { - if (!reference.isLeaf() && state.shouldSubdivideReference()) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - reference._children[i]->writeSpanners(nextState); - } - } - return; - } - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - if (reference.isLeaf() || !state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSpanners(nextState); - } - return; - } - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (_children[i] != reference._children[i]) { - _children[i]->writeSpannerDelta(*reference._children[i], nextState); - - } else if (nextState.becameSubdivided()) { - _children[i]->writeSpannerSubdivision(nextState); - } - } -} - -void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const { - if (!isLeaf()) { - MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f }; - if (!state.shouldSubdivideReference()) { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i]->writeSpanners(nextState); - } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - _children[i]->writeSpannerSubdivision(nextState); - } - } - } - } -} - void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) { if (!_referenceCount.deref()) { destroy(attribute); diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 56d9dd3a8a..806c5bf2ae 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -239,9 +239,7 @@ public: void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const; void writeSpanners(MetavoxelStreamState& state) const; - void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; - void writeSpannerSubdivision(MetavoxelStreamState& state) const; - + /// Increments the node's reference count. void incrementReferenceCount() { _referenceCount.ref(); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 956dc45729..f7c34b4a04 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1114,6 +1114,76 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } } +HeightfieldStack::HeightfieldStack(int width, const QVector& materials) : + HeightfieldData(width), + _materials(materials) { +} + +HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes) { + read(in, bytes); +} + +HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_materials, reference->getMaterials()); + reference->setDeltaData(DataBlockPointer(this)); + _width = reference->getWidth(); +} + +void HeightfieldStack::write(Bitstream& out) { + out << _materials; +} + +void HeightfieldStack::writeDelta(Bitstream& out, const HeightfieldStackPointer& reference) { +} + +void HeightfieldStack::read(Bitstream& in, int bytes) { + in >> _materials; +} + +Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value) { + if (value) { + value->write(out); + } else { + out << 0; + } + return out; +} + +Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value) { + int size; + in >> size; + if (size == 0) { + value = HeightfieldStackPointer(); + } else { + value = new HeightfieldStack(in, size); + } + return in; +} + +template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference) { + if (value) { + value->writeDelta(*this, reference); + } else { + *this << 0; + } +} + +template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference) { + int size; + *this >> size; + if (size == 0) { + value = HeightfieldStackPointer(); + } else { + value = new HeightfieldStack(*this, size, reference); + } +} + bool HeightfieldStreamState::shouldSubdivide() const { return base.lod.shouldSubdivide(minimum, size); } @@ -1144,10 +1214,11 @@ void HeightfieldStreamState::setMinimum(const glm::vec2& lastMinimum, int index) } HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material) : + const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack) : _height(height), _color(color), _material(material), + _stack(stack), _renderer(NULL) { } @@ -1155,6 +1226,7 @@ HeightfieldNode::HeightfieldNode(const HeightfieldNode& other) : _height(other.getHeight()), _color(other.getColor()), _material(other.getMaterial()), + _stack(other.getStack()), _renderer(NULL) { for (int i = 0; i < CHILD_COUNT; i++) { @@ -1169,7 +1241,7 @@ HeightfieldNode::~HeightfieldNode() { const int HEIGHT_LEAF_SIZE = 256 + HeightfieldHeight::HEIGHT_EXTENSION; void HeightfieldNode::setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material) { + const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack) { clearChildren(); int heightWidth = height->getWidth(); @@ -1246,9 +1318,13 @@ void HeightfieldNode::setContents(const HeightfieldHeightPointer& height, const childMaterial = new HeightfieldMaterial(childMaterialWidth, childMaterialContents, childMaterials); } + HeightfieldStackPointer childStack; + if (stack) { + } + _children[i] = new HeightfieldNode(); _children[i]->setContents(HeightfieldHeightPointer(new HeightfieldHeight(childHeightWidth, childHeightContents)), - childColor, childMaterial); + childColor, childMaterial, childStack); } mergeChildren(); @@ -2046,13 +2122,13 @@ void HeightfieldNode::read(HeightfieldStreamState& state) { clearChildren(); if (!state.shouldSubdivide()) { - state.base.stream >> _height >> _color >> _material; + state.base.stream >> _height >> _color >> _material >> _stack; return; } bool leaf; state.base.stream >> leaf; if (leaf) { - state.base.stream >> _height >> _color >> _material; + state.base.stream >> _height >> _color >> _material >> _stack; } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2067,13 +2143,13 @@ void HeightfieldNode::read(HeightfieldStreamState& state) { void HeightfieldNode::write(HeightfieldStreamState& state) const { if (!state.shouldSubdivide()) { - state.base.stream << _height << _color << _material; + state.base.stream << _height << _color << _material << _stack; return; } bool leaf = isLeaf(); state.base.stream << leaf; if (leaf) { - state.base.stream << _height << _color << _material; + state.base.stream << _height << _color << _material << _stack; } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2091,6 +2167,7 @@ void HeightfieldNode::readDelta(const HeightfieldNodePointer& reference, Heightf state.base.stream.readDelta(_height, reference->getHeight()); state.base.stream.readDelta(_color, reference->getColor()); state.base.stream.readDelta(_material, reference->getMaterial()); + state.base.stream.readDelta(_stack, reference->getStack()); return; } bool leaf; @@ -2099,6 +2176,7 @@ void HeightfieldNode::readDelta(const HeightfieldNodePointer& reference, Heightf state.base.stream.readDelta(_height, reference->getHeight()); state.base.stream.readDelta(_color, reference->getColor()); state.base.stream.readDelta(_material, reference->getMaterial()); + state.base.stream.readDelta(_stack, reference->getStack()); } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2135,6 +2213,7 @@ void HeightfieldNode::writeDelta(const HeightfieldNodePointer& reference, Height state.base.stream.writeDelta(_height, reference->getHeight()); state.base.stream.writeDelta(_color, reference->getColor()); state.base.stream.writeDelta(_material, reference->getMaterial()); + state.base.stream.writeDelta(_stack, reference->getStack()); return; } bool leaf = isLeaf(); @@ -2143,6 +2222,7 @@ void HeightfieldNode::writeDelta(const HeightfieldNodePointer& reference, Height state.base.stream.writeDelta(_height, reference->getHeight()); state.base.stream.writeDelta(_color, reference->getColor()); state.base.stream.writeDelta(_material, reference->getMaterial()); + state.base.stream.writeDelta(_material, reference->getStack()); } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2174,10 +2254,10 @@ HeightfieldNode* HeightfieldNode::readSubdivision(HeightfieldStreamState& state) bool leaf; state.base.stream >> leaf; if (leaf) { - return isLeaf() ? this : new HeightfieldNode(_height, _color, _material); + return isLeaf() ? this : new HeightfieldNode(_height, _color, _material, _stack); } else { - HeightfieldNode* newNode = new HeightfieldNode(_height, _color, _material); + HeightfieldNode* newNode = new HeightfieldNode(_height, _color, _material, _stack); HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); @@ -2207,7 +2287,7 @@ HeightfieldNode* HeightfieldNode::readSubdivision(HeightfieldStreamState& state) return node; } } else if (!isLeaf()) { - return new HeightfieldNode(_height, _color, _material); + return new HeightfieldNode(_height, _color, _material, _stack); } return this; } @@ -2243,13 +2323,13 @@ void HeightfieldNode::readSubdivided(HeightfieldStreamState& state, const Height if (!state.shouldSubdivide()) { // TODO: subdivision encoding - state.base.stream >> _height >> _color >> _material; + state.base.stream >> _height >> _color >> _material >> _stack; return; } bool leaf; state.base.stream >> leaf; if (leaf) { - state.base.stream >> _height >> _color >> _material; + state.base.stream >> _height >> _color >> _material >> _stack; } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2266,13 +2346,13 @@ void HeightfieldNode::writeSubdivided(HeightfieldStreamState& state, const Heigh const HeightfieldNode* ancestor) const { if (!state.shouldSubdivide()) { // TODO: subdivision encoding - state.base.stream << _height << _color << _material; + state.base.stream << _height << _color << _material << _stack; return; } bool leaf = isLeaf(); state.base.stream << leaf; if (leaf) { - state.base.stream << _height << _color << _material; + state.base.stream << _height << _color << _material << _stack; } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2516,6 +2596,7 @@ Heightfield::Heightfield() : connect(this, &Heightfield::heightChanged, this, &Heightfield::updateRoot); connect(this, &Heightfield::colorChanged, this, &Heightfield::updateRoot); connect(this, &Heightfield::materialChanged, this, &Heightfield::updateRoot); + connect(this, &Heightfield::stackChanged, this, &Heightfield::updateRoot); updateRoot(); } @@ -2549,9 +2630,9 @@ void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { } } -void Heightfield::setRoot(const HeightfieldNodePointer& root) { - if (_root != root) { - emit rootChanged(_root = root); +void Heightfield::setStack(const HeightfieldStackPointer& stack) { + if (_stack != stack) { + emit stackChanged(_stack = stack); } } @@ -2948,7 +3029,7 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float void Heightfield::writeExtra(Bitstream& out) const { if (getWillBeVoxelized()) { - out << _height << _color << _material; + out << _height << _color << _material << _stack; return; } MetavoxelLOD lod; @@ -2962,7 +3043,7 @@ void Heightfield::writeExtra(Bitstream& out) const { void Heightfield::readExtra(Bitstream& in) { if (getWillBeVoxelized()) { - in >> _height >> _color >> _material; + in >> _height >> _color >> _material >> _stack; return; } MetavoxelLOD lod; @@ -3072,7 +3153,7 @@ void Heightfield::updateBounds() { void Heightfield::updateRoot() { HeightfieldNodePointer root(new HeightfieldNode()); if (_height) { - root->setContents(_height, _color, _material); + root->setContents(_height, _color, _material, _stack); } setRoot(root); } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index bec1355b48..2e95d54528 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -23,6 +23,7 @@ class HeightfieldColor; class HeightfieldHeight; class HeightfieldMaterial; class HeightfieldNode; +class HeightfieldStack; class SpannerRenderer; /// An object that spans multiple octree cells. @@ -466,6 +467,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value); template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); +typedef QExplicitlySharedDataPointer HeightfieldStackPointer; + +/// A block of stack data associated with a heightfield. +class HeightfieldStack : public HeightfieldData { +public: + + HeightfieldStack(int width, const QVector& materials); + HeightfieldStack(Bitstream& in, int bytes); + HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference); + + QVector& getMaterials() { return _materials; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QVector _materials; +}; + +Q_DECLARE_METATYPE(HeightfieldStackPointer) + +Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value); +Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value); + +template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference); +template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference); + typedef QExplicitlySharedDataPointer HeightfieldNodePointer; /// Holds the base state used in streaming heightfield data. @@ -499,14 +530,15 @@ public: HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(), const HeightfieldColorPointer& color = HeightfieldColorPointer(), - const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer()); + const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer(), + const HeightfieldStackPointer& stack = HeightfieldStackPointer()); HeightfieldNode(const HeightfieldNode& other); ~HeightfieldNode(); void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, - const HeightfieldMaterialPointer& material); + const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack); void setHeight(const HeightfieldHeightPointer& height) { _height = height; } const HeightfieldHeightPointer& getHeight() const { return _height; } @@ -517,6 +549,9 @@ public: void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; } const HeightfieldMaterialPointer& getMaterial() const { return _material; } + void setStack(const HeightfieldStackPointer& stack) { _stack = stack; } + const HeightfieldStackPointer& getStack() const { return _stack; } + void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; } AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; } @@ -566,6 +601,7 @@ private: HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; + HeightfieldStackPointer _stack; HeightfieldNodePointer _children[CHILD_COUNT]; @@ -588,8 +624,9 @@ class Heightfield : public Transformable { 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) - Q_PROPERTY(HeightfieldNodePointer root MEMBER _root WRITE setRoot NOTIFY rootChanged STORED false DESIGNABLE false) - + Q_PROPERTY(HeightfieldStackPointer stack MEMBER _stack WRITE setStack NOTIFY stackChanged STORED false + DESIGNABLE false) + public: Q_INVOKABLE Heightfield(); @@ -609,7 +646,10 @@ public: void setMaterial(const HeightfieldMaterialPointer& material); const HeightfieldMaterialPointer& getMaterial() const { return _material; } - void setRoot(const HeightfieldNodePointer& root); + void setStack(const HeightfieldStackPointer& stack); + const HeightfieldStackPointer& getStack() const { return _stack; } + + void setRoot(const HeightfieldNodePointer& root) { _root = root; } const HeightfieldNodePointer& getRoot() const { return _root; } MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; @@ -652,7 +692,7 @@ signals: void heightChanged(const HeightfieldHeightPointer& height); void colorChanged(const HeightfieldColorPointer& color); void materialChanged(const HeightfieldMaterialPointer& material); - void rootChanged(const HeightfieldNodePointer& root); + void stackChanged(const HeightfieldStackPointer& stack); protected: @@ -671,6 +711,7 @@ private: HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; + HeightfieldStackPointer _stack; HeightfieldNodePointer _root; }; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 3f08cdec69..35fcee80a7 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 10; + return 11; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From a94372efc789ad2a9b55e89a82748c1adbfa1325 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 8 Dec 2014 16:10:07 -0800 Subject: [PATCH 03/67] Working on stack data implementation. --- libraries/metavoxels/src/Spanner.cpp | 125 ++++++++++++++++++++++++++- libraries/metavoxels/src/Spanner.h | 4 +- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f7c34b4a04..fa0f2590be 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1114,8 +1114,48 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } } -HeightfieldStack::HeightfieldStack(int width, const QVector& materials) : +const quint16 EMPTY_HEIGHT = 0; +const int BYTES_PER_LAYER = sizeof(quint16) + 4 + 1; + +static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, int height, + const QVector& contents) { + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = width; + *header++ = height; + foreach (const QByteArray& stack, contents) { + inflated.append(stack); + inflated.append((const char*)&EMPTY_HEIGHT, sizeof(quint16)); + } + return qCompress(inflated); +} + +static QVector decodeHeightfieldStack(const QByteArray& encoded, + int& offsetX, int& offsetY, int& width, int& height) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + width = *header++; + height = *header++; + const char* src = inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; + QVector contents(width * height); + for (QByteArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { + while (*(const quint16*)src != EMPTY_HEIGHT) { + dest->append(src, BYTES_PER_LAYER); + src += BYTES_PER_LAYER; + } + src += sizeof(quint16); + } + return contents; +} + +HeightfieldStack::HeightfieldStack(int width, const QVector& contents, + const QVector& materials) : HeightfieldData(width), + _contents(contents), _materials(materials) { } @@ -1124,7 +1164,7 @@ HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes) { } HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference) { - if (!reference) { + if (!reference) { read(in, bytes); return; } @@ -1133,16 +1173,97 @@ HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldSt in.readDelta(_materials, reference->getMaterials()); reference->setDeltaData(DataBlockPointer(this)); _width = reference->getWidth(); + _contents = reference->getContents(); + + int offsetX, offsetY, width, height; + QVector delta = decodeHeightfieldStack(reference->getEncodedDelta(), offsetX, offsetY, width, height); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _width = width; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + const QByteArray* src = delta.constData(); + QByteArray* dest = _contents.data() + minY * _width + minX; + for (int y = 0; y < height; y++, src += width, dest += _width) { + const QByteArray* lineSrc = src; + for (QByteArray* lineDest = dest, *end = dest + width; lineDest != end; lineDest++, lineSrc++) { + *lineDest = *lineSrc; + } + } } void HeightfieldStack::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + _encoded = encodeHeightfieldStack(0, 0, _width, _contents.size() / _width, _contents); + } + out << _encoded.size(); + out.writeAligned(_encoded); out << _materials; } void HeightfieldStack::writeDelta(Bitstream& out, const HeightfieldStackPointer& reference) { + if (!reference) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + if (reference->getWidth() != _width || reference->getContents().size() != _contents.size()) { + reference->setEncodedDelta(encodeHeightfieldStack(0, 0, _width, _contents.size() / _width, _contents)); + + } else { + int height = _contents.size() / _width; + int minX = _width, minY = height; + int maxX = -1, maxY = -1; + const QByteArray* src = _contents.constData(); + const QByteArray* ref = reference->getContents().constData(); + for (int y = 0; y < height; y++) { + bool difference = false; + for (int x = 0; x < _width; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + difference = true; + } + } + if (difference) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + QVector delta; + int deltaWidth = 0, deltaHeight = 0; + if (maxX >= minX) { + deltaWidth = maxX - minX + 1; + deltaHeight = maxY - minY + 1; + delta = QVector(deltaWidth * deltaHeight); + QByteArray* dest = delta.data(); + src = _contents.constData() + minY * _width + minX; + for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { + const QByteArray* lineSrc = src; + for (QByteArray* lineDest = dest, *end = dest + deltaWidth; lineDest != end; lineDest++, lineSrc++) { + *lineDest = *lineSrc; + } + } + } + reference->setEncodedDelta(encodeHeightfieldStack(minX + 1, minY + 1, deltaWidth, deltaHeight, delta)); + } + reference->setDeltaData(DataBlockPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_materials, reference->getMaterials()); } void HeightfieldStack::read(Bitstream& in, int bytes) { + int offsetX, offsetY, height; + _contents = decodeHeightfieldStack(_encoded = in.readAligned(bytes), offsetX, offsetY, _width, height); in >> _materials; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 2e95d54528..6682f1e6dd 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -473,10 +473,11 @@ typedef QExplicitlySharedDataPointer HeightfieldStackPointer; class HeightfieldStack : public HeightfieldData { public: - HeightfieldStack(int width, const QVector& materials); + HeightfieldStack(int width, const QVector& contents, const QVector& materials); HeightfieldStack(Bitstream& in, int bytes); HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference); + QVector& getContents() { return _contents; } QVector& getMaterials() { return _materials; } void write(Bitstream& out); @@ -486,6 +487,7 @@ private: void read(Bitstream& in, int bytes); + QVector _contents; QVector _materials; }; From 8ccb44b71eb8d910558ecd048dc311da9241ac19 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 9 Dec 2014 16:24:07 -0800 Subject: [PATCH 04/67] Bits of stack implementation. --- interface/src/MetavoxelSystem.cpp | 39 +++++++++++++++++++++++----- interface/src/MetavoxelSystem.h | 34 ++++++++++++------------ libraries/metavoxels/src/Spanner.cpp | 21 +++++++++++++++ libraries/metavoxels/src/Spanner.h | 11 ++++++++ 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 87060d7dfa..08a85df090 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -363,7 +363,7 @@ void MetavoxelSystem::render() { _baseVoxelProgram.bind(); - foreach (const VoxelBatch& batch, _voxelBaseBatches) { + foreach (const MetavoxelBatch& batch, _voxelBaseBatches) { batch.vertexBuffer->bind(); batch.indexBuffer->bind(); @@ -712,7 +712,7 @@ int BufferCursorRenderVisitor::visit(MetavoxelInfo& info) { } BufferData* buffer = info.inputValues.at(0).getInlineValue().data(); if (buffer) { - buffer->render(true); + buffer->render(glm::vec3(), glm::quat(), glm::vec3(1.0f, 1.0f, 1.0f), true); } return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; } @@ -1163,7 +1163,7 @@ bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::ve return false; } -void VoxelBuffer::render(bool cursor) { +void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!_vertexBuffer.isCreated()) { _vertexBuffer.create(); _vertexBuffer.bind(); @@ -1203,7 +1203,10 @@ void VoxelBuffer::render(bool cursor) { return; } - VoxelBatch baseBatch; + MetavoxelBatch baseBatch; + baseBatch.translation = translation; + baseBatch.rotation = rotation; + baseBatch.scale = scale; baseBatch.vertexBuffer = &_vertexBuffer; baseBatch.indexBuffer = &_indexBuffer; baseBatch.vertexCount = _vertexCount; @@ -1212,6 +1215,9 @@ void VoxelBuffer::render(bool cursor) { if (!_materials.isEmpty()) { VoxelSplatBatch splatBatch; + splatBatch.translation = translation; + splatBatch.rotation = rotation; + splatBatch.scale = scale; splatBatch.vertexBuffer = &_vertexBuffer; splatBatch.indexBuffer = &_indexBuffer; splatBatch.vertexCount = _vertexCount; @@ -1948,7 +1954,7 @@ int BufferRenderVisitor::visit(MetavoxelInfo& info) { } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); if (buffer) { - buffer->render(); + buffer->render(glm::vec3(), glm::quat(), glm::vec3(1.0f, 1.0f, 1.0f)); } return STOP_RECURSION; } @@ -2195,7 +2201,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - const QVector& heightContents = node->getHeight()->getContents(); + QVector heightContents = node->getHeight()->getContents(); + if (node->getStack()) { + // clear any height values covered by stacks + const QByteArray* src = node->getStack()->getContents().constData(); + for (quint16* dest = heightContents.data(), *end = dest + heightContents.size(); dest != end; dest++, src++) { + if (!src->isEmpty()) { + *dest = 0; + } + } + } glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, heightContents.constData()); @@ -2242,6 +2257,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } glBindTexture(GL_TEXTURE_2D, 0); } + if (!_voxels && node->getStack()) { + QVector vertices; + QVector indices; + QVector hermiteSegments; + QMultiHash quadIndices; + + _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, width, node->getStack()->getMaterials()); + } + + if (_voxels) { + _voxels->render(translation, rotation, scale, cursor); + } if (cursor) { bufferPair.first.bind(); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 26f3bd68a3..c9a750ad3a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -26,8 +26,8 @@ class HeightfieldBaseLayerBatch; class HeightfieldSplatBatch; class HermiteBatch; +class MetavoxelBatch; class Model; -class VoxelBatch; class VoxelSplatBatch; /// Renders a metavoxel tree. @@ -86,7 +86,7 @@ public: void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } - void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); } + void addVoxelBaseBatch(const MetavoxelBatch& batch) { _voxelBaseBatches.append(batch); } void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); } @@ -123,7 +123,7 @@ private: QVector _heightfieldBaseBatches; QVector _heightfieldSplatBatches; - QVector _voxelBaseBatches; + QVector _voxelBaseBatches; QVector _voxelSplatBatches; QVector _hermiteBatches; @@ -166,8 +166,8 @@ private: static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); }; -/// Base class for heightfield batches. -class HeightfieldBatch { +/// Base class for all batches. +class MetavoxelBatch { public: QOpenGLBuffer* vertexBuffer; QOpenGLBuffer* indexBuffer; @@ -176,6 +176,11 @@ public: glm::vec3 scale; int vertexCount; int indexCount; +}; + +/// Base class for heightfield batches. +class HeightfieldBatch : public MetavoxelBatch { +public: GLuint heightTextureID; glm::vec4 heightScale; }; @@ -199,17 +204,8 @@ public: int materialIndex; }; -/// Base class for voxel batches. -class VoxelBatch { -public: - QOpenGLBuffer* vertexBuffer; - QOpenGLBuffer* indexBuffer; - int vertexCount; - int indexCount; -}; - /// A batch containing a voxel splat. -class VoxelSplatBatch : public VoxelBatch { +class VoxelSplatBatch : public MetavoxelBatch { public: int splatTextureIDs[4]; glm::vec4 splatTextureScalesS; @@ -295,7 +291,8 @@ public: virtual ~BufferData(); - virtual void render(bool cursor = false) = 0; + virtual void render(const glm::vec3& translation, const glm::quat& rotation, + const glm::vec3& scale, bool cursor = false) = 0; }; typedef QExplicitlySharedDataPointer BufferDataPointer; @@ -340,7 +337,8 @@ public: bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - virtual void render(bool cursor = false); + virtual void render(const glm::vec3& translation, const glm::quat& rotation, + const glm::vec3& scale, bool cursor = false); private: @@ -460,6 +458,8 @@ private: GLuint _materialTextureID; QVector _networkTextures; + BufferDataPointer _voxels; + typedef QPair IntPair; typedef QPair BufferPair; static QHash _bufferPairs; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index fa0f2590be..a976f56ae5 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -123,6 +123,11 @@ Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& he return this; } +Spanner* Spanner::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color) { + return this; +} + bool Spanner::hasOwnColors() const { return false; } @@ -2239,6 +2244,11 @@ HeightfieldNode* HeightfieldNode::clearAndFetchHeight(const glm::vec3& translati return newNode; } +HeightfieldNode* HeightfieldNode::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color) { + return this; +} + void HeightfieldNode::read(HeightfieldStreamState& state) { clearChildren(); @@ -2843,6 +2853,17 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer return newHeightfield; } +Spanner* Heightfield::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color) { + HeightfieldNode* newRoot = _root->sculptMaterial(spanner, material, color); + if (_root == newRoot) { + return this; + } + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); + return newHeightfield; +} + bool Heightfield::hasOwnColors() const { return _color; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6682f1e6dd..083dff3c5f 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -86,6 +86,11 @@ public: /// \return the modified spanner, or this if no modification was performed virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + /// Attempts to "sculpt" with the supplied spanner. + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color); + /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -577,6 +582,9 @@ public: HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& bounds, SharedObjectPointer& heightfield); + + HeightfieldNode* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color); void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -671,6 +679,9 @@ public: virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); + virtual Spanner* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + const QColor& color); + virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; virtual QRgb getColorAt(const glm::vec3& point); From dfcb22bd7ac06c5f68767232771dea9ee1b59c5c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 12 Dec 2014 15:12:03 -0800 Subject: [PATCH 05/67] More progress towards stacks. --- interface/src/MetavoxelSystem.cpp | 18 +- interface/src/MetavoxelSystem.h | 8 +- interface/src/ui/MetavoxelEditor.cpp | 146 +++++----------- interface/src/ui/MetavoxelEditor.h | 88 +++------- .../metavoxels/src/AttributeRegistry.cpp | 1 - .../metavoxels/src/MetavoxelMessages.cpp | 158 ++---------------- libraries/metavoxels/src/MetavoxelMessages.h | 25 +-- libraries/metavoxels/src/Spanner.cpp | 41 ++++- libraries/metavoxels/src/Spanner.h | 8 +- 9 files changed, 123 insertions(+), 370 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 08a85df090..c3734b6349 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -559,23 +559,13 @@ void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float applyMaterialEdit(edit, true); } -void MetavoxelSystem::paintVoxelColor(const glm::vec3& position, float radius, const QColor& color) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, SharedObjectPointer(), color)) }; +void MetavoxelSystem::setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color) { + MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) }; applyEdit(edit, true); } -void MetavoxelSystem::paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintVoxelMaterialEdit(position, radius, material)) }; - applyMaterialEdit(edit, true); -} - -void MetavoxelSystem::setVoxelColor(const SharedObjectPointer& spanner, const QColor& color) { - MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) }; - applyEdit(edit, true); -} - -void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) { - MetavoxelEditMessage edit = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, material)) }; +void MetavoxelSystem::setHeightfieldMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) { + MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, material)) }; applyMaterialEdit(edit, true); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index e04bb6a772..78d5bd39ef 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -76,13 +76,9 @@ public: Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); - Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color); - - Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); + Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color); - Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color); - - Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); + Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 97c4c08b41..21d8d8abaf 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -122,13 +122,12 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new InsertSpannerTool(this)); addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); + addTool(new ImportHeightfieldTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); - addTool(new ImportHeightfieldTool(this)); - addTool(new VoxelMaterialBoxTool(this)); - addTool(new VoxelMaterialSpannerTool(this)); - addTool(new VoxelMaterialBrushTool(this)); - addTool(new VoxelSculptBrushTool(this)); + addTool(new HeightfieldSculptBrushTool(this)); + addTool(new HeightfieldMaterialBoxTool(this)); + addTool(new HeightfieldMaterialSpannerTool(this)); updateAttributes(); @@ -944,8 +943,26 @@ QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) { } } -VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) : - BoxTool(editor, "Set Voxel Material (Box)", false) { +HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) : + HeightfieldBrushTool(editor, "Sculpt Brush"), + _materialControl(new MaterialControl(this, _form, true)) { +} + +QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) { + Sphere* sphere = new Sphere(); + sphere->setTranslation(_position); + sphere->setScale(_radius->value()); + if (alternate) { + return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), + SharedObjectPointer(), QColor(0, 0, 0, 0))); + } else { + return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), + _materialControl->getMaterial(), _materialControl->getColor())); + } +} + +HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) : + BoxTool(editor, "Set Material (Box)", false) { QWidget* widget = new QWidget(); QFormLayout* form = new QFormLayout(); @@ -962,32 +979,32 @@ VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) : _materialControl = new MaterialControl(this, form, true); } -bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("VoxelColorAttribute"); +bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("SpannerSetAttribute"); } -bool VoxelMaterialBoxTool::shouldSnapToGrid() { +bool HeightfieldMaterialBoxTool::shouldSnapToGrid() { return _snapToGrid->isChecked(); } -QColor VoxelMaterialBoxTool::getColor() { +QColor HeightfieldMaterialBoxTool::getColor() { return _materialControl->getColor(); } -void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { +void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { Cuboid* cuboid = new Cuboid(); cuboid->setTranslation((maximum + minimum) * 0.5f); glm::vec3 vector = (maximum - minimum) * 0.5f; cuboid->setScale(vector.x); cuboid->setAspectY(vector.y / vector.x); cuboid->setAspectZ(vector.z / vector.x); - MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid), + MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid), _materialControl->getMaterial(), _materialControl->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } -VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) : - PlaceSpannerTool(editor, "Set Voxel Material (Spanner)", QString(), false) { +HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* editor) : + PlaceSpannerTool(editor, "Set Material (Spanner)", QString(), false) { QWidget* widget = new QWidget(); QFormLayout* form = new QFormLayout(); @@ -1001,110 +1018,25 @@ VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) : QPushButton* place = new QPushButton("Set"); layout()->addWidget(place); - connect(place, &QPushButton::clicked, this, &VoxelMaterialSpannerTool::place); + connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place); } -bool VoxelMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("VoxelColorAttribute"); +bool HeightfieldMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("SpannerSetAttribute"); } -SharedObjectPointer VoxelMaterialSpannerTool::getSpanner() { +SharedObjectPointer HeightfieldMaterialSpannerTool::getSpanner() { return _spannerEditor->getObject(); } -QColor VoxelMaterialSpannerTool::getColor() { +QColor HeightfieldMaterialSpannerTool::getColor() { return _materialControl->getColor(); } -void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { +void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { static_cast(spanner.data())->setWillBeVoxelized(true); - MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, + MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, _materialControl->getMaterial(), _materialControl->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } -VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false, true), - _positionValid(false) { - - QWidget* widget = new QWidget(); - widget->setLayout(_form = new QFormLayout()); - layout()->addWidget(widget); - - _form->addRow("Radius:", _radius = new QDoubleSpinBox()); - _radius->setSingleStep(0.01); - _radius->setMaximum(FLT_MAX); - _radius->setValue(0.25); -} - -bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const { - return attribute->inherits("VoxelColorAttribute"); -} - -void VoxelBrushTool::render() { - if (Application::getInstance()->isMouseHidden()) { - return; - } - - // find the intersection with the voxels - glm::vec3 origin = Application::getInstance()->getMouseRayOrigin(); - glm::vec3 direction = Application::getInstance()->getMouseRayDirection(); - - float heightfieldDistance = FLT_MAX, voxelDistance = FLT_MAX; - if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection( - origin, direction, heightfieldDistance) | - Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) { - _positionValid = false; - return; - } - _positionValid = true; - Application::getInstance()->getMetavoxels()->renderVoxelCursor( - _position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value()); -} - -bool VoxelBrushTool::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)); - return true; - - } else if (event->type() == QEvent::MouseButtonPress && _positionValid) { - MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - return true; - } - return false; -} - -VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) : - VoxelBrushTool(editor, "Material Brush"), - _materialControl(new MaterialControl(this, _form)) { -} - -QVariant VoxelMaterialBrushTool::createEdit(bool alternate) { - if (alternate) { - return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); - } else { - return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), - _materialControl->getMaterial(), _materialControl->getColor())); - } -} - -VoxelSculptBrushTool::VoxelSculptBrushTool(MetavoxelEditor* editor) : - VoxelBrushTool(editor, "Sculpt Brush"), - _materialControl(new MaterialControl(this, _form, true)) { -} - -QVariant VoxelSculptBrushTool::createEdit(bool alternate) { - Sphere* sphere = new Sphere(); - sphere->setTranslation(_position); - sphere->setScale(_radius->value()); - if (alternate) { - return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere), - SharedObjectPointer(), QColor(0, 0, 0, 0))); - } else { - return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere), - _materialControl->getMaterial(), _materialControl->getColor())); - } -} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index c154d7bc59..4389a97512 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -386,13 +386,30 @@ private: MaterialControl* _materialControl; }; -/// Allows setting voxel materials by dragging out a box. -class VoxelMaterialBoxTool : public BoxTool { +/// Allows sculpting parts of the heightfield. +class HeightfieldSculptBrushTool : public HeightfieldBrushTool { Q_OBJECT public: - VoxelMaterialBoxTool(MetavoxelEditor* editor); + HeightfieldSculptBrushTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(bool alternate); + +private: + + MaterialControl* _materialControl; +}; + +/// Allows setting heightfield materials by dragging out a box. +class HeightfieldMaterialBoxTool : public BoxTool { + Q_OBJECT + +public: + + HeightfieldMaterialBoxTool(MetavoxelEditor* editor); virtual bool appliesTo(const AttributePointer& attribute) const; @@ -410,13 +427,13 @@ private: MaterialControl* _materialControl; }; -/// Allows setting voxel materials by placing a spanner. -class VoxelMaterialSpannerTool : public PlaceSpannerTool { +/// Allows setting heightfield materials by placing a spanner. +class HeightfieldMaterialSpannerTool : public PlaceSpannerTool { Q_OBJECT public: - VoxelMaterialSpannerTool(MetavoxelEditor* editor); + HeightfieldMaterialSpannerTool(MetavoxelEditor* editor); virtual bool appliesTo(const AttributePointer& attribute) const; @@ -432,63 +449,4 @@ private: MaterialControl* _materialControl; }; -/// Base class for voxel brush tools. -class VoxelBrushTool : public MetavoxelTool { - Q_OBJECT - -public: - - VoxelBrushTool(MetavoxelEditor* editor, const QString& name); - - virtual bool appliesTo(const AttributePointer& attribute) const; - - virtual void render(); - - virtual bool eventFilter(QObject* watched, QEvent* event); - -protected: - - virtual QVariant createEdit(bool alternate) = 0; - - QFormLayout* _form; - QDoubleSpinBox* _radius; - - glm::vec3 _position; - bool _positionValid; -}; - -/// Allows texturing parts of the voxel field. -class VoxelMaterialBrushTool : public VoxelBrushTool { - Q_OBJECT - -public: - - VoxelMaterialBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - MaterialControl* _materialControl; -}; - -/// Allows sculpting parts of the voxel field. -class VoxelSculptBrushTool : public VoxelBrushTool { - Q_OBJECT - -public: - - VoxelSculptBrushTool(MetavoxelEditor* editor); - -protected: - - virtual QVariant createEdit(bool alternate); - -private: - - MaterialControl* _materialControl; -}; - #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index afdc0c923f..b10d162b34 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -53,7 +53,6 @@ AttributeRegistry::AttributeRegistry() : const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f; _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); - _voxelColorAttribute->setUserFacing(true); _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); _voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1225752df7..ab0fe34552 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -199,16 +199,16 @@ const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1; const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES; const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES; -VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner, +HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& averageColor) : MaterialEdit(material, averageColor), spanner(spanner) { } -class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor { +class HeightfieldMaterialSpannerEditVisitor : public MetavoxelVisitor { public: - VoxelMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color); + HeightfieldMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color); virtual int visit(MetavoxelInfo& info); @@ -220,7 +220,7 @@ private: float _blockSize; }; -VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanner, +HeightfieldMaterialSpannerEditVisitor::HeightfieldMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << @@ -234,7 +234,7 @@ VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanne _blockSize(spanner->getVoxelizationGranularity() * VOXEL_BLOCK_SIZE) { } -int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { +int HeightfieldMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { Box bounds = info.getBounds(); if (!bounds.intersects(_spanner->getBounds())) { return STOP_RECURSION; @@ -492,154 +492,20 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { 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()); - while (!data.getBounds().contains(spanner->getBounds())) { - data.expand(); - } - // make sure it's either 100% transparent or 100% opaque +void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // make sure the color is either 100% transparent or 100% opaque QColor color = averageColor; color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); - // 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); + data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), + static_cast(spanner.data())->getBounds(), 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); + Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color); + if (newResult != result) { + data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult); } } - - // voxelize the fetched heightfield, if any - if (heightfield) { - VoxelMaterialSpannerEditVisitor visitor(static_cast(heightfield.data()), material, color); - data.guide(visitor); - } - - VoxelMaterialSpannerEditVisitor visitor(spanner, material, color); - data.guide(visitor); } -PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& averageColor) : - MaterialEdit(material, averageColor), - position(position), - radius(radius) { -} - -class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor { -public: - - PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color); - - virtual int visit(MetavoxelInfo& info); - -private: - - glm::vec3 _position; - float _radius; - SharedObjectPointer _material; - QColor _color; - Box _bounds; -}; - -PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), - _position(position), - _radius(radius), - _material(material), - _color(color) { - - glm::vec3 extents(_radius, _radius, _radius); - _bounds = Box(_position - extents, _position + extents); -} - -int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); - if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) { - return STOP_RECURSION; - } - QVector colorContents = colorPointer->getContents(); - QByteArray materialContents = materialPointer->getContents(); - QVector materials = materialPointer->getMaterials(); - - Box overlap = info.getBounds().getIntersection(_bounds); - int size = colorPointer->getSize(); - int area = size * size; - float scale = (size - 1.0f) / info.size; - overlap.minimum = (overlap.minimum - info.minimum) * scale; - overlap.maximum = (overlap.maximum - info.minimum) * scale; - int minX = glm::ceil(overlap.minimum.x); - int minY = glm::ceil(overlap.minimum.y); - int minZ = glm::ceil(overlap.minimum.z); - int sizeX = (int)overlap.maximum.x - minX + 1; - int sizeY = (int)overlap.maximum.y - minY + 1; - int sizeZ = (int)overlap.maximum.z - minZ + 1; - - QRgb rgb = _color.rgba(); - float step = 1.0f / scale; - glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step); - uchar materialIndex = getMaterialIndex(_material, materials, materialContents); - QRgb* colorData = colorContents.data(); - uchar* materialData = (uchar*)materialContents.data(); - for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ; - destZ += area, position.z += step) { - position.y = info.minimum.y + minY * step; - for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) { - position.x = info.minimum.x + minX * step; - for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) { - QRgb& color = colorData[destX]; - if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) { - color = rgb; - materialData[destX] = materialIndex; - } - } - } - } - VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size)); - info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), - encodeInline(newColorPointer)); - - clearUnusedMaterials(materials, materialContents); - VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials)); - info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline(newMaterialPointer)); - - return STOP_RECURSION; -} - -void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // make sure it's 100% opaque - QColor color = averageColor; - color.setAlphaF(1.0f); - PaintVoxelMaterialEditVisitor visitor(position, radius, material, color); - data.guide(visitor); -} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 10477e0486..00c4353603 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -242,37 +242,20 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit) -/// An edit that sets the materials of voxels within a spanner to a value. -class VoxelMaterialSpannerEdit : STREAM public MaterialEdit { +/// An edit that sets the materials of a heightfield within a spanner to a value. +class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit { STREAMABLE public: STREAM SharedObjectPointer spanner; - VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), + HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; -DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit) - -/// An edit that sets a region of a voxel material. -class PaintVoxelMaterialEdit : STREAM public MaterialEdit { - STREAMABLE - -public: - - STREAM glm::vec3 position; - STREAM float radius; - - PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, - const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit) +DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit) #endif // hifi_MetavoxelMessages_h diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index a976f56ae5..d743155a1e 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -123,7 +123,7 @@ Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& he return this; } -Spanner* Spanner::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, +Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color) { return this; } @@ -2244,8 +2244,36 @@ HeightfieldNode* HeightfieldNode::clearAndFetchHeight(const glm::vec3& translati return newNode; } -HeightfieldNode* HeightfieldNode::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color) { +HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color) { + Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); + if (!bounds.intersects(spanner->getBounds())) { + return this; + } + if (!isLeaf()) { + HeightfieldNode* newNode = this; + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); + 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); + if (_children[i] != newChild) { + if (newNode == this) { + newNode = new HeightfieldNode(*this); + } + newNode->setChild(i, HeightfieldNodePointer(newChild)); + } + } + if (newNode != this) { + newNode->mergeChildren(); + } + return newNode; + } + if (!_height) { + return this; + } + return this; } @@ -2353,7 +2381,7 @@ void HeightfieldNode::writeDelta(const HeightfieldNodePointer& reference, Height state.base.stream.writeDelta(_height, reference->getHeight()); state.base.stream.writeDelta(_color, reference->getColor()); state.base.stream.writeDelta(_material, reference->getMaterial()); - state.base.stream.writeDelta(_material, reference->getStack()); + state.base.stream.writeDelta(_stack, reference->getStack()); } else { HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; @@ -2853,9 +2881,10 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer return newHeightfield; } -Spanner* Heightfield::sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, +Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color) { - HeightfieldNode* newRoot = _root->sculptMaterial(spanner, material, color); + HeightfieldNode* newRoot = _root->setMaterial(getTranslation(), getRotation(), glm::vec3(getScale(), + getScale() * _aspectY, getScale() * _aspectZ), static_cast(spanner.data()), material, color); if (_root == newRoot) { return this; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 083dff3c5f..b7998d59c0 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -88,7 +88,7 @@ public: /// Attempts to "sculpt" with the supplied spanner. /// \return the modified spanner, or this if no modification was performed - virtual Spanner* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color); /// Checks whether this spanner has its own colors. @@ -583,8 +583,8 @@ public: HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& bounds, SharedObjectPointer& heightfield); - HeightfieldNode* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color); + HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color); void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -679,7 +679,7 @@ public: virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); - virtual Spanner* sculptMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, + virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color); virtual bool hasOwnColors() const; From 51f82b4cd9d882be0eb7c27c07873795a9da6fcb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 13 Dec 2014 17:49:55 -0800 Subject: [PATCH 06/67] More stack bits. --- interface/src/MetavoxelSystem.cpp | 12 +++- libraries/metavoxels/src/Spanner.cpp | 91 +++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index c3734b6349..f40a685c88 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2194,10 +2194,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g QVector heightContents = node->getHeight()->getContents(); if (node->getStack()) { // clear any height values covered by stacks + int stackWidth = node->getStack()->getWidth(); + int stackHeight = node->getStack()->getContents().size() / stackWidth; const QByteArray* src = node->getStack()->getContents().constData(); - for (quint16* dest = heightContents.data(), *end = dest + heightContents.size(); dest != end; dest++, src++) { - if (!src->isEmpty()) { - *dest = 0; + quint16* dest = heightContents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; + for (int z = 0; z < stackHeight; z++, dest += width) { + quint16* lineDest = dest; + for (int x = 0; x < stackWidth; x++, src++, lineDest++) { + if (!src->isEmpty()) { + *lineDest = 0; + } } } } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index d743155a1e..f338b3a2be 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1734,7 +1734,7 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const colorContents = QByteArray(baseWidth * baseHeight * DataBlock::COLOR_BYTES, 0xFF); } - int materialWidth = baseWidth, materialHeight = baseHeight; + int materialWidth = colorWidth, materialHeight = colorHeight; QByteArray materialContents; QVector materials; if (_material) { @@ -1744,7 +1744,7 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const materials = _material->getMaterials(); } else { - materialContents = QByteArray(baseWidth * baseHeight, 0); + materialContents = QByteArray(materialWidth * materialHeight, 0); } int highestX = colorWidth - 1; @@ -2273,8 +2273,93 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons 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; + int highestHeightX = heightWidth - 1; + int highestHeightZ = heightHeight - 1; + QVector newHeightContents = _height->getContents(); - return this; + int colorWidth, colorHeight; + QByteArray newColorContents; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + newColorContents = _color->getContents(); + + } else { + colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; + colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; + newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF); + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + + int materialWidth, materialHeight; + QByteArray newMaterialContents; + QVector newMaterialMaterials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + newMaterialContents = _material->getContents(); + newMaterialMaterials = _material->getMaterials(); + + } else { + materialWidth = colorWidth; + materialHeight = colorHeight; + newMaterialContents = QByteArray(materialWidth * materialHeight, 0); + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + + int stackWidth, stackHeight; + QVector newStackContents; + QVector newStackMaterials; + if (_stack) { + stackWidth = _stack->getWidth(); + stackHeight = _stack->getContents().size() / stackWidth; + newStackContents = _stack->getContents(); + newStackMaterials = _stack->getMaterials(); + + } else { + stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; + stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; + newStackContents = QVector(stackWidth * stackHeight); + } + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; + + glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; + Box transformedBounds = inverseTransform * spanner->getBounds(); + glm::mat4 transform = glm::inverse(inverseTransform); + + glm::vec3 start = glm::floor(transformedBounds.minimum); + glm::vec3 end = glm::ceil(transformedBounds.maximum); + + float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); + float startY = glm::clamp(start.y, 0.0f, (float)numeric_limits::max()); + float endY = glm::clamp(end.y, 0.0f, (float)numeric_limits::max()); + float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); + quint16* lineDest = newHeightContents.data() + (int)startZ * heightWidth + (int)startX; + glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, startY, startZ, 1.0f)); + glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); + bool erase = (color.alpha() == 0); + for (float z = startZ; z <= endZ; z += 1.0f, worldStart += worldStepZ, lineDest += heightWidth) { + quint16* dest = lineDest; + glm::vec3 worldPos = worldStart; + for (float x = startX; x <= endX; x += 1.0f, dest++, worldPos += worldStepX) { + *dest = 32768; + } + } + + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), + HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, newMaterialContents, newMaterialMaterials)), + HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } void HeightfieldNode::read(HeightfieldStreamState& state) { From a4c30be41414e5b7bf58dea44fdd7046f3406bcc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 15 Dec 2014 13:56:32 -0800 Subject: [PATCH 07/67] More spanner set bits. --- libraries/metavoxels/src/Spanner.cpp | 107 +++++++++++++++++++++++---- libraries/metavoxels/src/Spanner.h | 6 +- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f338b3a2be..d00fa3747c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2244,10 +2244,48 @@ HeightfieldNode* HeightfieldNode::clearAndFetchHeight(const glm::vec3& translati return newNode; } -HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - Spanner* spanner, const SharedObjectPointer& material, const QColor& color) { +void HeightfieldNode::getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const Box& spannerBounds, bool erase, int& minimum, int& maximum) const { Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); - if (!bounds.intersects(spanner->getBounds())) { + if (!bounds.intersects(spannerBounds)) { + return; + } + if (!isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); + _children[i]->getRangeAfterMaterialSet(translation + + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, + i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, + nextScale, spannerBounds, erase, minimum, maximum); + } + return; + } + if (!_height) { + return; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + + glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; + Box transformedBounds = inverseTransform * spannerBounds; + + glm::vec3 start = glm::floor(transformedBounds.minimum); + glm::vec3 end = glm::ceil(transformedBounds.maximum); + + minimum = qMin(minimum, (int)start.y); + maximum = qMax(maximum, (int)end.y); +} + +HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, + float normalizeScale, float normalizeOffset) { + Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); + bool intersects = bounds.intersects(spanner->getBounds()); + if (!intersects && normalizeScale == 1.0f && normalizeOffset == 0.0f) { return this; } if (!isLeaf()) { @@ -2257,7 +2295,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); + nextScale, spanner, material, color, normalizeScale, normalizeOffset); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -2281,6 +2319,21 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int highestHeightZ = heightHeight - 1; QVector newHeightContents = _height->getContents(); + // renormalize if necessary + if (normalizeScale != 1.0f || normalizeOffset != 0.0f) { + for (quint16* dest = newHeightContents.data(), *end = newHeightContents.data() + newHeightContents.size(); + dest != end; dest++) { + int value = *dest; + if (value != 0) { + *dest = (value + normalizeOffset) * normalizeScale; + } + } + } + if (!intersects) { + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + _color, _material, _stack); + } + int colorWidth, colorHeight; QByteArray newColorContents; if (_color) { @@ -2340,19 +2393,33 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 end = glm::ceil(transformedBounds.maximum); float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); - float startY = glm::clamp(start.y, 0.0f, (float)numeric_limits::max()); - float endY = glm::clamp(end.y, 0.0f, (float)numeric_limits::max()); float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); quint16* lineDest = newHeightContents.data() + (int)startZ * heightWidth + (int)startX; - glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, startY, startZ, 1.0f)); + glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, start.y, startZ, 1.0f)); glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, end.y - start.y, 0.0f, 0.0f)); glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); bool erase = (color.alpha() == 0); for (float z = startZ; z <= endZ; z += 1.0f, worldStart += worldStepZ, lineDest += heightWidth) { quint16* dest = lineDest; glm::vec3 worldPos = worldStart; for (float x = startX; x <= endX; x += 1.0f, dest++, worldPos += worldStepX) { - *dest = 32768; + glm::vec3 endPos = worldPos + worldStepY; + float distance; + glm::vec3 normal; + if (erase) { + if (spanner->intersects(worldPos, endPos, distance, normal)) { + quint16 height = glm::round(glm::mix(start.y, end.y, distance)); + if (height <= *dest) { + *dest = height; + } + } + } else if (spanner->intersects(endPos, worldPos, distance, normal)) { + quint16 height = glm::round(glm::mix(end.y, start.y, distance)); + if (height >= *dest) { + *dest = height; + } + } } } @@ -2968,13 +3035,25 @@ Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color) { - HeightfieldNode* newRoot = _root->setMaterial(getTranslation(), getRotation(), glm::vec3(getScale(), - getScale() * _aspectY, getScale() * _aspectZ), static_cast(spanner.data()), material, color); - if (_root == newRoot) { - return this; - } + // first see if we're going to exceed the range limits + Spanner* spannerData = static_cast(spanner.data()); + int minimumValue = 1, maximumValue = numeric_limits::max(); + _root->getRangeAfterMaterialSet(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + getScale() * _aspectZ), spannerData->getBounds(), color.alpha() == 0, minimumValue, maximumValue); + + // renormalize if necessary Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); + float normalizeScale = 1.0f, normalizeOffset = 0.0f; + if (minimumValue < 1 || maximumValue > numeric_limits::max()) { + normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); + normalizeOffset = 1.0f - minimumValue; + newHeightfield->setAspectY(_aspectY / normalizeScale); + newHeightfield->setTranslation(getTranslation() - getRotation() * + glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); + } + newHeightfield->setRoot(HeightfieldNodePointer(_root->setMaterial(newHeightfield->getTranslation(), getRotation(), + glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), spannerData, + material, color, normalizeScale, normalizeOffset))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index b7998d59c0..140d8ffc8c 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -583,8 +583,12 @@ public: HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& bounds, SharedObjectPointer& heightfield); + void getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const Box& spannerBounds, bool erase, int& minimum, int& maximum) const; + HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - Spanner* spanner, const SharedObjectPointer& material, const QColor& color); + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, + float normalizeScale, float normalizeOffset); void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; From b62ad547edf0204e042c25395f65599a81335588 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 15 Dec 2014 18:31:01 -0800 Subject: [PATCH 08/67] More work on spanner edits. --- libraries/metavoxels/src/Spanner.cpp | 78 ++++++++++++++++++++++------ libraries/metavoxels/src/Spanner.h | 2 +- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index d00fa3747c..d6f3749916 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2245,7 +2245,7 @@ HeightfieldNode* HeightfieldNode::clearAndFetchHeight(const glm::vec3& translati } void HeightfieldNode::getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& spannerBounds, bool erase, int& minimum, int& maximum) const { + const Box& spannerBounds, int& minimum, int& maximum) const { Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); if (!bounds.intersects(spannerBounds)) { return; @@ -2256,7 +2256,7 @@ void HeightfieldNode::getRangeAfterMaterialSet(const glm::vec3& translation, con _children[i]->getRangeAfterMaterialSet(translation + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, spannerBounds, erase, minimum, maximum); + nextScale, spannerBounds, minimum, maximum); } return; } @@ -2380,9 +2380,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; newStackContents = QVector(stackWidth * stackHeight); } - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; - glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; @@ -2392,36 +2389,85 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 start = glm::floor(transformedBounds.minimum); glm::vec3 end = glm::ceil(transformedBounds.maximum); + float stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); + float stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); + float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); - quint16* lineDest = newHeightContents.data() + (int)startZ * heightWidth + (int)startX; glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, start.y, startZ, 1.0f)); - glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, end.y - start.y, 0.0f, 0.0f)); - glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); + glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); bool erase = (color.alpha() == 0); - for (float z = startZ; z <= endZ; z += 1.0f, worldStart += worldStepZ, lineDest += heightWidth) { - quint16* dest = lineDest; + char r = color.red(), g = color.green(), b = color.blue(); + uchar materialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); + bool hasOwnColors = spanner->hasOwnColors(); + bool hasOwnMaterials = spanner->hasOwnMaterials(); + QHash materialMappings; + for (float z = startZ; z <= endZ; z += stepZ, worldStart += worldStepZ) { + quint16* heightDest = newHeightContents.data() + (int)z * heightWidth; glm::vec3 worldPos = worldStart; - for (float x = startX; x <= endX; x += 1.0f, dest++, worldPos += worldStepX) { + for (float x = startX; x <= endX; x += stepX, worldPos += worldStepX) { + quint16* heightLineDest = heightDest + (int)x; glm::vec3 endPos = worldPos + worldStepY; float distance; glm::vec3 normal; if (erase) { if (spanner->intersects(worldPos, endPos, distance, normal)) { quint16 height = glm::round(glm::mix(start.y, end.y, distance)); - if (height <= *dest) { - *dest = height; + if (height <= *heightLineDest) { + *heightLineDest = height; } } } else if (spanner->intersects(endPos, worldPos, distance, normal)) { quint16 height = glm::round(glm::mix(end.y, start.y, distance)); - if (height >= *dest) { - *dest = height; + if (height >= *heightLineDest) { + *heightLineDest = height; + + float colorX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerColorWidth / innerHeightWidth; + float colorZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerColorHeight / innerHeightHeight; + if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { + char* colorDest = newColorContents.data() + ((int)colorZ * colorWidth + + (int)colorX) * DataBlock::COLOR_BYTES; + if (hasOwnColors) { + QRgb spannerColor = spanner->getColorAt(glm::mix(endPos, worldPos, distance)); + colorDest[0] = qRed(spannerColor); + colorDest[1] = qGreen(spannerColor); + colorDest[2] = qBlue(spannerColor); + + } else { + colorDest[0] = r; + colorDest[1] = g; + colorDest[2] = b; + } + } + + float materialX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialWidth / innerHeightWidth; + float materialZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialHeight / innerHeightHeight; + if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && + materialZ <= innerMaterialHeight) { + char* materialDest = newMaterialContents.data() + (int)materialZ * materialWidth + (int)materialX; + if (hasOwnMaterials) { + int index = spanner->getMaterialAt(glm::mix(endPos, worldPos, distance)); + if (index != 0) { + int& mapping = materialMappings[index]; + if (mapping == 0) { + mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), + newMaterialMaterials, newMaterialContents); + } + index = mapping; + } + *materialDest = index; + + } else { + *materialDest = materialIndex; + } + } } } } } + clearUnusedMaterials(newMaterialMaterials, newMaterialContents); return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), @@ -3039,7 +3085,7 @@ Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const Shar Spanner* spannerData = static_cast(spanner.data()); int minimumValue = 1, maximumValue = numeric_limits::max(); _root->getRangeAfterMaterialSet(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), spannerData->getBounds(), color.alpha() == 0, minimumValue, maximumValue); + getScale() * _aspectZ), spannerData->getBounds(), minimumValue, maximumValue); // renormalize if necessary Heightfield* newHeightfield = static_cast(clone(true)); diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 140d8ffc8c..0032840083 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -584,7 +584,7 @@ public: const Box& bounds, SharedObjectPointer& heightfield); void getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& spannerBounds, bool erase, int& minimum, int& maximum) const; + const Box& spannerBounds, int& minimum, int& maximum) const; HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, Spanner* spanner, const SharedObjectPointer& material, const QColor& color, From 682800c618f13ea8a572ba9f67cc1fb088bbd95d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 16 Dec 2014 17:06:05 -0800 Subject: [PATCH 09/67] Working on stack edits. --- libraries/metavoxels/src/Spanner.cpp | 89 ++++++++++++++++++++++++---- libraries/metavoxels/src/Spanner.h | 2 + 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index d6f3749916..4297471119 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1119,9 +1119,6 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } } -const quint16 EMPTY_HEIGHT = 0; -const int BYTES_PER_LAYER = sizeof(quint16) + 4 + 1; - static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, int height, const QVector& contents) { QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); @@ -1129,10 +1126,11 @@ static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, in *header++ = offsetX; *header++ = offsetY; *header++ = width; - *header++ = height; + *header++ = height; foreach (const QByteArray& stack, contents) { - inflated.append(stack); - inflated.append((const char*)&EMPTY_HEIGHT, sizeof(quint16)); + quint16 entries = stack.size() / HeightfieldStack::ENTRY_BYTES; + inflated.append((const char*)&entries, sizeof(quint16)); + inflated.append(stack); } return qCompress(inflated); } @@ -1148,15 +1146,17 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, const char* src = inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; QVector contents(width * height); for (QByteArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { - while (*(const quint16*)src != EMPTY_HEIGHT) { - dest->append(src, BYTES_PER_LAYER); - src += BYTES_PER_LAYER; - } + int bytes = *(const quint16*)src * HeightfieldStack::ENTRY_BYTES; src += sizeof(quint16); + *dest = QByteArray(src, bytes); + src += bytes; } return contents; } +// RGBA color, material, Hermite values for X, Y, and Z +const int HeightfieldStack::ENTRY_BYTES = 4 + 1 + 4 * 3; + HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), @@ -2380,6 +2380,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; newStackContents = QVector(stackWidth * stackHeight); } + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; + glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; @@ -2398,8 +2401,13 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, end.y - start.y, 0.0f, 0.0f)); glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); - bool erase = (color.alpha() == 0); - char r = color.red(), g = color.green(), b = color.blue(); + float voxelStep = scale.x / innerHeightWidth; + float voxelScale = scale.y / (numeric_limits::max() * voxelStep); + glm::vec3 voxelStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); + int newTop = end.y * voxelScale; + int newBottom = start.y * voxelScale; + char r = color.red(), g = color.green(), b = color.blue(), a = color.alpha(); + bool erase = (a == 0); uchar materialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); @@ -2412,6 +2420,63 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 endPos = worldPos + worldStepY; float distance; glm::vec3 normal; + + float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; + float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; + if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { + QByteArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; + quint16 oldHeight = *heightLineDest; + int offset = 0; + if (oldHeight != 0 && !stackDest->isEmpty()) { + int oldTop = oldHeight * voxelScale; + offset = qMax(0, oldTop - newTop) * HeightfieldStack::ENTRY_BYTES; + int prepend = qMax(0, newTop - oldTop) * HeightfieldStack::ENTRY_BYTES; + int append = qMax(0, (oldTop - stackDest->size() + 1) - newBottom) * HeightfieldStack::ENTRY_BYTES; + if (prepend != 0 || append != 0) { + QByteArray newStack(prepend + stackDest->size() + append, 0); + memcpy(newStack.data() + prepend, stackDest->constData(), stackDest->size()); + *stackDest = newStack; + } + } else { + *stackDest = QByteArray((newTop - newBottom + 1) * HeightfieldStack::ENTRY_BYTES, 0); + } + char* entryDest = stackDest->data() + offset; + glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; + for (int y = newTop; y >= newBottom; y--, entryDest += HeightfieldStack::ENTRY_BYTES, pos -= voxelStepY) { + if (!spanner->contains(pos)) { + continue; + } + if (hasOwnColors && !erase) { + QRgb spannerColor = spanner->getColorAt(pos); + entryDest[0] = qRed(spannerColor); + entryDest[1] = qGreen(spannerColor); + entryDest[2] = qBlue(spannerColor); + entryDest[3] = qAlpha(spannerColor); + + } else { + entryDest[0] = r; + entryDest[1] = g; + entryDest[2] = b; + entryDest[3] = a; + } + if (hasOwnMaterials && !erase) { + int index = spanner->getMaterialAt(pos); + if (index != 0) { + int& mapping = materialMappings[index]; + if (mapping == 0) { + mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), + newMaterialMaterials, newMaterialContents); + } + index = mapping; + } + entryDest[4] = index; + + } else { + entryDest[4] = materialIndex; + } + } + } + if (erase) { if (spanner->intersects(worldPos, endPos, distance, normal)) { quint16 height = glm::round(glm::mix(start.y, end.y, distance)); diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 0032840083..7c5ef9e0c0 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -478,6 +478,8 @@ typedef QExplicitlySharedDataPointer HeightfieldStackPointer; class HeightfieldStack : public HeightfieldData { public: + static const int ENTRY_BYTES; + HeightfieldStack(int width, const QVector& contents, const QVector& materials); HeightfieldStack(Bitstream& in, int bytes); HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference); From 068d6035c02bc8eca8bbd9d653259e1eed44ade6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 16 Dec 2014 20:39:49 -0800 Subject: [PATCH 10/67] More progress on stack edits. --- libraries/metavoxels/src/Spanner.cpp | 114 +++++++++++++++++---------- 1 file changed, 74 insertions(+), 40 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 4297471119..dd9a962f8e 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2389,8 +2389,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons Box transformedBounds = inverseTransform * spanner->getBounds(); glm::mat4 transform = glm::inverse(inverseTransform); - glm::vec3 start = glm::floor(transformedBounds.minimum); - glm::vec3 end = glm::ceil(transformedBounds.maximum); + glm::vec3 start = glm::ceil(transformedBounds.maximum); + glm::vec3 end = glm::floor(transformedBounds.minimum); float stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); float stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); @@ -2399,25 +2399,25 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, start.y, startZ, 1.0f)); glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); - glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, end.y - start.y, 0.0f, 0.0f)); + glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, start.y - end.y, 0.0f, 0.0f)); glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); float voxelStep = scale.x / innerHeightWidth; float voxelScale = scale.y / (numeric_limits::max() * voxelStep); glm::vec3 voxelStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); - int newTop = end.y * voxelScale; - int newBottom = start.y * voxelScale; + int newTop = start.y * voxelScale; + int newBottom = end.y * voxelScale; char r = color.red(), g = color.green(), b = color.blue(), a = color.alpha(); bool erase = (a == 0); uchar materialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); QHash materialMappings; - for (float z = startZ; z <= endZ; z += stepZ, worldStart += worldStepZ) { + for (float z = startZ; z >= endZ; z -= stepZ, worldStart -= worldStepZ) { quint16* heightDest = newHeightContents.data() + (int)z * heightWidth; glm::vec3 worldPos = worldStart; - for (float x = startX; x <= endX; x += stepX, worldPos += worldStepX) { + for (float x = startX; x >= endX; x -= stepX, worldPos -= worldStepX) { quint16* heightLineDest = heightDest + (int)x; - glm::vec3 endPos = worldPos + worldStepY; + glm::vec3 endPos = worldPos - worldStepY; float distance; glm::vec3 normal; @@ -2443,49 +2443,83 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons char* entryDest = stackDest->data() + offset; glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; for (int y = newTop; y >= newBottom; y--, entryDest += HeightfieldStack::ENTRY_BYTES, pos -= voxelStepY) { - if (!spanner->contains(pos)) { - continue; - } - if (hasOwnColors && !erase) { - QRgb spannerColor = spanner->getColorAt(pos); - entryDest[0] = qRed(spannerColor); - entryDest[1] = qGreen(spannerColor); - entryDest[2] = qBlue(spannerColor); - entryDest[3] = qAlpha(spannerColor); - - } else { - entryDest[0] = r; - entryDest[1] = g; - entryDest[2] = b; - entryDest[3] = a; - } - if (hasOwnMaterials && !erase) { - int index = spanner->getMaterialAt(pos); - if (index != 0) { - int& mapping = materialMappings[index]; - if (mapping == 0) { - mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), - newMaterialMaterials, newMaterialContents); - } - index = mapping; + if (spanner->contains(pos)) { + if (hasOwnColors && !erase) { + QRgb spannerColor = spanner->getColorAt(pos); + entryDest[0] = qRed(spannerColor); + entryDest[1] = qGreen(spannerColor); + entryDest[2] = qBlue(spannerColor); + entryDest[3] = qAlpha(spannerColor); + + } else { + entryDest[0] = r; + entryDest[1] = g; + entryDest[2] = b; + entryDest[3] = a; } - entryDest[4] = index; + if (hasOwnMaterials && !erase) { + int index = spanner->getMaterialAt(pos); + if (index != 0) { + int& mapping = materialMappings[index]; + if (mapping == 0) { + mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), + newMaterialMaterials, newMaterialContents); + } + index = mapping; + } + entryDest[4] = index; + + } else { + entryDest[4] = materialIndex; + } + } + int nextStackX = (int)stackX + 1; + if (nextStackX <= innerStackWidth) { + QByteArray* nextStackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)nextStackX; + + } + if (entryDest != stackDest->data()) { + bool previousSet = ((entryDest - HeightfieldStack::ENTRY_BYTES)[3] != 0); + bool currentSet = (entryDest[3] != 0); + if (previousSet == currentSet) { + entryDest[9] = entryDest[10] = entryDest[11] = entryDest[12] = 0; + } else { + bool intersects; + if (erase == previousSet) { + if ((intersects = spanner->intersects(pos - voxelStepY, pos, distance, normal))) { + distance = 1.0f - distance; + } + } else { + intersects = spanner->intersects(pos, pos - voxelStepY, distance, normal); + } + if (intersects) { + if (erase) { + normal = -normal; + } + entryDest[9] = normal.x * numeric_limits::max(); + entryDest[10] = normal.y * numeric_limits::max(); + entryDest[11] = normal.z * numeric_limits::max(); + entryDest[12] = distance * numeric_limits::max(); + } + } + } + int nextStackZ = (int)stackZ + 1; + if (nextStackZ <= innerStackHeight) { + QByteArray* nextStackDest = newStackContents.data() + (int)nextStackZ * stackWidth + (int)stackX; - } else { - entryDest[4] = materialIndex; } } } if (erase) { - if (spanner->intersects(worldPos, endPos, distance, normal)) { - quint16 height = glm::round(glm::mix(start.y, end.y, distance)); + if (spanner->intersects(endPos, worldPos, distance, normal)) { + quint16 height = glm::round(glm::mix(end.y, start.y, distance)); if (height <= *heightLineDest) { *heightLineDest = height; } } - } else if (spanner->intersects(endPos, worldPos, distance, normal)) { - quint16 height = glm::round(glm::mix(end.y, start.y, distance)); + } else if (spanner->intersects(worldPos, endPos, distance, normal)) { + quint16 height = glm::round(glm::mix(start.y, end.y, distance)); if (height >= *heightLineDest) { *heightLineDest = height; From 70a0bd07abeb2d6b339f4520bdebb4d05eab0dd7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 17 Dec 2014 17:56:52 -0800 Subject: [PATCH 11/67] Various editing bits. --- libraries/metavoxels/src/Spanner.cpp | 622 ++++++++------------------- libraries/metavoxels/src/Spanner.h | 30 +- 2 files changed, 191 insertions(+), 461 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index dd9a962f8e..db0517e83d 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -119,10 +119,6 @@ Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float hei return this; } -Spanner* Spanner::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { - return this; -} - Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color) { return this; @@ -1128,7 +1124,8 @@ static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, in *header++ = width; *header++ = height; foreach (const QByteArray& stack, contents) { - quint16 entries = stack.size() / HeightfieldStack::ENTRY_BYTES; + quint16 entries = stack.isEmpty() ? 0 : (stack.size() - HeightfieldStack::POSITION_BYTES) / + HeightfieldStack::ENTRY_BYTES; inflated.append((const char*)&entries, sizeof(quint16)); inflated.append(stack); } @@ -1146,14 +1143,19 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, const char* src = inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; QVector contents(width * height); for (QByteArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { - int bytes = *(const quint16*)src * HeightfieldStack::ENTRY_BYTES; + int entries = *(const quint16*)src; src += sizeof(quint16); - *dest = QByteArray(src, bytes); - src += bytes; + if (entries > 0) { + int bytes = HeightfieldStack::POSITION_BYTES + entries * HeightfieldStack::ENTRY_BYTES; + *dest = QByteArray(src, bytes); + src += bytes; + } } return contents; } +const int HeightfieldStack::POSITION_BYTES = sizeof(quint16); + // RGBA color, material, Hermite values for X, Y, and Z const int HeightfieldStack::ENTRY_BYTES = 4 + 1 + 4 * 3; @@ -1824,59 +1826,58 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const return newNode; } -void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& position, const glm::vec3& radius, - float height, int& minimum, int& maximum) const { - if (position.x + radius.x < 0.0f || position.z + radius.z < 0.0f || - position.x - radius.x > 1.0f || position.z - radius.z > 1.0f) { - return; - } - if (!isLeaf()) { - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->getRangeAfterHeightPaint(position * glm::vec3(2.0f, 1.0f, 2.0f) - - glm::vec3(i & X_MAXIMUM_FLAG ? 1.0f : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? 1.0f : 0.0f), - radius * glm::vec3(2.0f, 1.0f, 2.0f), height, minimum, maximum); - } - return; - } +void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const { if (!_height) { return; } 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; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int highestHeightX = heightWidth - 1; + int highestHeightZ = heightHeight - 1; - glm::vec3 scale((float)innerWidth, 1.0f, (float)innerHeight); - glm::vec3 center = position * scale; - center.x += 1.0f; - center.z += 1.0f; + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); + glm::vec3 extents = radius * inverseScale; - glm::vec3 extents = radius * scale; - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); + if (center.x + extents.x < 0.0f || center.z + extents.z < 0.0f || + center.x - extents.x > highestHeightX || center.z - extents.z > highestHeightZ) { + return; + } + if (!isLeaf()) { + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); + _children[i]->getRangeAfterHeightPaint(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, minimum, maximum); + } + return; + } + glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); - // first see if we're going to exceed the range limits - 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; + const quint16* lineDest = _height->getContents().constData() + (int)start.z * heightWidth + (int)start.x; float squaredRadius = extents.x * extents.x; float squaredRadiusReciprocal = 1.0f / squaredRadius; float multiplierZ = extents.x / extents.z; - 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 relativeHeight = height * numeric_limits::max() / scale.y; + for (float z = start.z; z <= end.z; z += 1.0f) { + const quint16* dest = lineDest; + for (float x = start.x; x <= end.x; 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; if (value != 0) { - value += height * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - minimum = qMin(minimum, value); - maximum = qMax(maximum, value); + value += relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + minimum = qMin(minimum, (float)value); + maximum = qMax(maximum, (float)value); } } } @@ -1884,18 +1885,35 @@ void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& position, const } } -HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& position, const glm::vec3& radius, - float height, float normalizeScale, float normalizeOffset) { - if ((position.x + radius.x < 0.0f || position.z + radius.z < 0.0f || position.x - radius.x > 1.0f || - position.z - radius.z > 1.0f) && normalizeScale == 1.0f && normalizeOffset == 0.0f) { +HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius, float height, float normalizeScale, float normalizeOffset) { + 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; + int highestHeightX = heightWidth - 1; + int highestHeightZ = heightHeight - 1; + + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); + glm::vec3 extents = radius * inverseScale; + + bool intersects = (center.x + extents.x >= 0.0f && center.z + extents.z >= 0.0f && + center.x - extents.x <= highestHeightX && center.z - extents.z <= highestHeightZ); + if (!intersects && normalizeScale == 1.0f && normalizeOffset == 0.0f) { return this; } if (!isLeaf()) { HeightfieldNode* newNode = this; for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldNode* newChild = _children[i]->paintHeight(position * glm::vec3(2.0f, 1.0f, 2.0f) - - glm::vec3(i & X_MAXIMUM_FLAG ? 1.0f : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? 1.0f : 0.0f), - radius * glm::vec3(2.0f, 1.0f, 2.0f), height, normalizeScale, normalizeOffset); + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); + 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, normalizeScale, normalizeOffset); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -1908,355 +1926,69 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& position, const g } return newNode; } - if (!_height) { - return this; + QVector newHeightContents = _height->getContents(); + + int stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; + QVector newStackContents; + QVector newStackMaterials; + if (_stack) { + stackWidth = _stack->getWidth(); + newStackContents = _stack->getContents(); + newStackMaterials = _stack->getMaterials(); } - 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; - - glm::vec3 scale((float)innerWidth, 1.0f, (float)innerHeight); - glm::vec3 center = position * scale; - center.x += 1.0f; - center.z += 1.0f; - - glm::vec3 extents = radius * scale; - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + // renormalize if necessary - bool changed = false; - if (normalizeScale != 1.0f || normalizeOffset != 0.0f) { - changed = true; - for (quint16* dest = contents.data(), *end = contents.data() + contents.size(); dest != end; dest++) { - int value = *dest; - if (value != 0) { - *dest = (value + normalizeOffset) * normalizeScale; - } - } + maybeRenormalize(scale, normalizeScale, normalizeOffset, innerStackWidth, newHeightContents, newStackContents); + if (!intersects) { + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } // now apply the actual change - 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; + glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + + quint16* lineDest = newHeightContents.data() + (int)start.z * heightWidth + (int)start.x; float squaredRadius = extents.x * extents.x; float squaredRadiusReciprocal = 1.0f / squaredRadius; float multiplierZ = extents.x / extents.z; - for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { + float relativeHeight = height * numeric_limits::max() / scale.y; + for (float z = start.z; z <= end.z; z += 1.0f) { quint16* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { + for (float x = start.x; x <= end.x; 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; if (value != 0) { - *dest = value + height * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - changed = true; + *dest = value + relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; } } } lineDest += heightWidth; } - if (!changed) { - return this; - } - HeightfieldNode* newNode = new HeightfieldNode(*this); - newNode->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); - return newNode; + + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } -HeightfieldNode* HeightfieldNode::clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, - const glm::vec3& scale, const Box& bounds, SharedObjectPointer& heightfield) { - Box nodeBounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); - if (!nodeBounds.intersects(bounds)) { - return this; - } - if (!isLeaf()) { - HeightfieldNode* newNode = this; - for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - HeightfieldNode* newChild = _children[i]->clearAndFetchHeight(translation + - rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, - i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, bounds, heightfield); - if (_children[i] != newChild) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - newNode->setChild(i, HeightfieldNodePointer(newChild)); - } - } - if (newNode != this) { - newNode->mergeChildren(); - } - return newNode; - } - 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 = scale.x / innerHeightWidth; - float heightIncrementZ = scale.z / 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 = scale.x / innerColorWidth; - float colorIncrementZ = scale.z / 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 = scale.x / innerMaterialWidth; - float materialIncrementZ = scale.z / 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, nodeBounds.minimum.y, - glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ); - glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, nodeBounds.maximum.y, - glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); - Box largestBounds(minimum, maximum); - - // enlarge the area to fetch - minimum.x -= largestIncrementX; - maximum.x += largestIncrementX; - minimum.z -= largestIncrementZ; - maximum.z += largestIncrementX; - - glm::mat4 baseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); - glm::vec3 inverseScale(innerHeightWidth / scale.x, 1.0f, innerHeightHeight / scale.z); - 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: - - 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 - 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; - - // create heightfield if necessary - Heightfield* spanner = static_cast(heightfield.data()); - if (!spanner) { - 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()))); - } - - // fetch the height - glm::vec3 spannerInverseScale((spannerHeightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / spanner->getScale(), 1.0f, - (spannerHeightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / (spanner->getScale() * spanner->getAspectZ())); - glm::mat4 spannerBaseTransform = glm::translate(-spanner->getTranslation()); - glm::mat4 spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; - Box spannerTransformedBounds = spannerTransform * nodeBounds; - int spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, - 0, spannerHeightWidth - 1); - int spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, - 0, spannerHeightHeight - 1); - int spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, - 0, spannerHeightWidth - 1); - int spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, - 0, spannerHeightHeight - 1); - quint16* dest = spanner->getHeight()->getContents().data() + spannerStartZ * spannerHeightWidth + spannerStartX; - glm::vec3 step = 1.0f / spannerInverseScale; - glm::vec3 initialPosition = glm::inverse(rotation) * (glm::vec3(spannerStartX - HeightfieldHeight::HEIGHT_BORDER, 0, - spannerStartZ - HeightfieldHeight::HEIGHT_BORDER) * step + spanner->getTranslation() - translation) / scale; - glm::vec3 position = initialPosition; - step = glm::inverse(rotation) * step / scale; - float heightScale = numeric_limits::max(); - for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerHeightWidth, position.z += step.z) { - quint16* lineDest = dest; - position.x = initialPosition.x; - for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { - float height = getHeight(position) * heightScale; - if (height > *lineDest) { - *lineDest = height; - } - } - } - - // and the color - if (_color) { - spannerInverseScale = glm::vec3((spannerColorWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, - (spannerColorHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); - spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; - spannerTransformedBounds = spannerTransform * nodeBounds; - spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerColorWidth - 1); - spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerColorHeight - 1); - spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerColorWidth - 1); - spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerColorHeight - 1); - - char* dest = spanner->getColor()->getContents().data() + - (spannerStartZ * spannerColorWidth + spannerStartX) * DataBlock::COLOR_BYTES; - step = 1.0f / spannerInverseScale; - initialPosition = glm::inverse(rotation) * (glm::vec3(spannerStartX, 0, spannerStartZ) * step + - spanner->getTranslation() - translation) / scale; - position = initialPosition; - step = glm::inverse(rotation) * step / scale; - for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerColorWidth * DataBlock::COLOR_BYTES, - position.z += step.z) { - char* lineDest = dest; - position.x = initialPosition.x; - for (int x = spannerStartX; x <= spannerEndX; x++, lineDest += DataBlock::COLOR_BYTES, position.x += step.x) { - QRgb color = getColorAt(position); - if (color != 0) { - lineDest[0] = qRed(color); - lineDest[1] = qGreen(color); - lineDest[2] = qBlue(color); - } - } - } - } - - // and the material - if (_material) { - spannerInverseScale = glm::vec3((spannerMaterialWidth - HeightfieldData::SHARED_EDGE) / spanner->getScale(), 1.0f, - (spannerMaterialHeight - HeightfieldData::SHARED_EDGE) / (spanner->getScale() * spanner->getAspectZ())); - spannerTransform = glm::scale(spannerInverseScale) * spannerBaseTransform; - spannerTransformedBounds = spannerTransform * nodeBounds; - spannerStartX = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.x), 0, spannerMaterialWidth - 1); - spannerStartZ = glm::clamp((int)glm::floor(spannerTransformedBounds.minimum.z), 0, spannerMaterialHeight - 1); - spannerEndX = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.x), 0, spannerMaterialWidth - 1); - spannerEndZ = glm::clamp((int)glm::ceil(spannerTransformedBounds.maximum.z), 0, spannerMaterialHeight - 1); - - char* dest = spanner->getMaterial()->getContents().data() + spannerStartZ * spannerMaterialWidth + spannerStartX; - step = 1.0f / spannerInverseScale; - initialPosition = glm::inverse(rotation) * (glm::vec3(spannerStartX, 0, spannerStartZ) * step + - spanner->getTranslation() - translation) / scale; - position = initialPosition; - step = glm::inverse(rotation) * step / scale; - QHash materialMap; - for (int z = spannerStartZ; z <= spannerEndZ; z++, dest += spannerMaterialWidth, position.z += step.z) { - char* lineDest = dest; - position.x = initialPosition.x; - for (int x = spannerStartX; x <= spannerEndX; x++, lineDest++, position.x += step.x) { - int material = getMaterialAt(position); - if (material != -1) { - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - material = mapping = getMaterialIndex(_material->getMaterials().at(material - 1), - spanner->getMaterial()->getMaterials(), spanner->getMaterial()->getContents()); - } - } - *lineDest = material; - } - } - } - } - - // clear the height - QVector newHeightContents = _height->getContents(); - dest = newHeightContents.data() + startZ * heightWidth + startX; - for (int z = startZ; z <= endZ; z++, dest += heightWidth) { - memset(dest, 0, (endX - startX + 1) * sizeof(quint16)); - } - - HeightfieldNode* newNode = new HeightfieldNode(); - newNode->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents))); - - // and the color - if (_color) { - inverseScale = glm::vec3(innerColorWidth / scale.x, 1.0f, innerColorHeight / scale.z); - 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); - } - newNode->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents))); - } - - // and the material - if (_material) { - inverseScale = glm::vec3(innerMaterialWidth / scale.x, 1.0f, innerMaterialHeight / scale.z); - 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); - newNode->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial( - materialWidth, newMaterialContents, newMaterials))); - } - - return newNode; -} - -void HeightfieldNode::getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& spannerBounds, int& minimum, int& maximum) const { +void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const Box& editBounds, float& minimum, float& maximum) const { Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); - if (!bounds.intersects(spannerBounds)) { + if (!bounds.intersects(editBounds)) { return; } if (!isLeaf()) { for (int i = 0; i < CHILD_COUNT; i++) { glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); - _children[i]->getRangeAfterMaterialSet(translation + + _children[i]->getRangeAfterEdit(translation + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, - nextScale, spannerBounds, minimum, maximum); + nextScale, editBounds, minimum, maximum); } return; } @@ -2271,13 +2003,13 @@ void HeightfieldNode::getRangeAfterMaterialSet(const glm::vec3& translation, con glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; - Box transformedBounds = inverseTransform * spannerBounds; + Box transformedBounds = inverseTransform * editBounds; glm::vec3 start = glm::floor(transformedBounds.minimum); glm::vec3 end = glm::ceil(transformedBounds.maximum); - minimum = qMin(minimum, (int)start.y); - maximum = qMax(maximum, (int)end.y); + minimum = qMin(minimum, start.y); + maximum = qMax(maximum, end.y); } HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, @@ -2319,19 +2051,28 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int highestHeightZ = heightHeight - 1; QVector newHeightContents = _height->getContents(); - // renormalize if necessary - if (normalizeScale != 1.0f || normalizeOffset != 0.0f) { - for (quint16* dest = newHeightContents.data(), *end = newHeightContents.data() + newHeightContents.size(); - dest != end; dest++) { - int value = *dest; - if (value != 0) { - *dest = (value + normalizeOffset) * normalizeScale; - } - } + int stackWidth, stackHeight; + QVector newStackContents; + QVector newStackMaterials; + if (_stack) { + stackWidth = _stack->getWidth(); + stackHeight = _stack->getContents().size() / stackWidth; + newStackContents = _stack->getContents(); + newStackMaterials = _stack->getMaterials(); + + } else { + stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; + stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; + newStackContents = QVector(stackWidth * stackHeight); } + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; + + // renormalize if necessary + maybeRenormalize(scale, normalizeScale, normalizeOffset, innerStackWidth, newHeightContents, newStackContents); if (!intersects) { return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, _stack); + _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } int colorWidth, colorHeight; @@ -2366,23 +2107,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - int stackWidth, stackHeight; - QVector newStackContents; - QVector newStackMaterials; - if (_stack) { - stackWidth = _stack->getWidth(); - stackHeight = _stack->getContents().size() / stackWidth; - newStackContents = _stack->getContents(); - newStackMaterials = _stack->getMaterials(); - - } else { - stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; - newStackContents = QVector(stackWidth * stackHeight); - } - int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; - glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; @@ -3035,6 +2759,29 @@ int HeightfieldNode::getMaterialAt(const glm::vec3& location) const { return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; } +void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, + int innerStackWidth, QVector& heightContents, QVector& stackContents) { + if (normalizeScale == 1.0f && normalizeOffset == 0.0f) { + return; + } + for (quint16* dest = heightContents.data(), *end = dest + heightContents.size(); dest != end; dest++) { + int value = *dest; + if (value != 0) { + *dest = (value + normalizeOffset) * normalizeScale; + } + } + if (stackContents.isEmpty()) { + return; + } + int stackOffset = glm::round(scale.y * normalizeOffset * normalizeScale * innerStackWidth / + (numeric_limits::max() * scale.x)); + for (QByteArray* dest = stackContents.data(), *end = dest + stackContents.size(); dest != end; dest++) { + if (!dest->isEmpty()) { + *(quint16*)dest->data() += stackOffset; + } + } +} + AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() { } @@ -3142,60 +2889,30 @@ Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { // first see if we're going to exceed the range limits - glm::vec3 inverseScale(1.0f / getScale(), 1.0f, 1.0f / (getScale() * _aspectZ)); - glm::vec3 relativePosition = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; - glm::vec3 relativeRadius = radius * inverseScale; - int minimumValue = 1, maximumValue = numeric_limits::max(); - _root->getRangeAfterHeightPaint(relativePosition, relativeRadius, - height * numeric_limits::max() / (getScale() * _aspectY), minimumValue, maximumValue); - - // renormalize if necessary - Heightfield* newHeightfield = static_cast(clone(true)); - float normalizeScale = 1.0f, normalizeOffset = 0.0f; - if (minimumValue < 1 || maximumValue > numeric_limits::max()) { - normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); - normalizeOffset = 1.0f - minimumValue; - newHeightfield->setAspectY(_aspectY / normalizeScale); - newHeightfield->setTranslation(getTranslation() - getRotation() * - glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); - } + float minimumValue = 1.0f, maximumValue = numeric_limits::max(); + _root->getRangeAfterHeightPaint(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + getScale() * _aspectZ), position, radius, height, minimumValue, maximumValue); - // now apply the actual change - newHeightfield->setRoot(HeightfieldNodePointer(_root->paintHeight(relativePosition, relativeRadius, - height * numeric_limits::max() / (getScale() * newHeightfield->getAspectY()), + // normalize if necessary + float normalizeScale, normalizeOffset; + 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, normalizeScale, normalizeOffset))); return newHeightfield; } -Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { - HeightfieldNode* newRoot = _root->clearAndFetchHeight(getTranslation(), getRotation(), - glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), bounds, heightfield); - if (_root == newRoot) { - return this; - } - Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); - return newHeightfield; -} - Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color) { // first see if we're going to exceed the range limits Spanner* spannerData = static_cast(spanner.data()); - int minimumValue = 1, maximumValue = numeric_limits::max(); - _root->getRangeAfterMaterialSet(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + float minimumValue = 1.0f, maximumValue = numeric_limits::max(); + _root->getRangeAfterEdit(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), spannerData->getBounds(), minimumValue, maximumValue); - - // renormalize if necessary - Heightfield* newHeightfield = static_cast(clone(true)); - float normalizeScale = 1.0f, normalizeOffset = 0.0f; - if (minimumValue < 1 || maximumValue > numeric_limits::max()) { - normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); - normalizeOffset = 1.0f - minimumValue; - newHeightfield->setAspectY(_aspectY / normalizeScale); - newHeightfield->setTranslation(getTranslation() - getRotation() * - glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); - } + + // normalize if necessary + float normalizeScale, normalizeOffset; + Heightfield* newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); newHeightfield->setRoot(HeightfieldNodePointer(_root->setMaterial(newHeightfield->getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), spannerData, material, color, normalizeScale, normalizeOffset))); @@ -3637,3 +3354,20 @@ void Heightfield::updateRoot() { } setRoot(root); } + +Heightfield* Heightfield::prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset) { + // renormalize if necessary + Heightfield* newHeightfield = static_cast(clone(true)); + if (minimumValue < 1.0f || maximumValue > numeric_limits::max()) { + normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); + normalizeOffset = 1.0f - minimumValue; + newHeightfield->setAspectY(_aspectY / normalizeScale); + newHeightfield->setTranslation(getTranslation() - getRotation() * + glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); + } else { + normalizeScale = 1.0f; + normalizeOffset = 0.0f; + } + return newHeightfield; +} + diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 7c5ef9e0c0..48e17161e4 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -81,11 +81,6 @@ 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); - /// Attempts to "sculpt" with the supplied spanner. /// \return the modified spanner, or this if no modification was performed virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, @@ -478,6 +473,7 @@ typedef QExplicitlySharedDataPointer HeightfieldStackPointer; class HeightfieldStack : public HeightfieldData { public: + static const int POSITION_BYTES; static const int ENTRY_BYTES; HeightfieldStack(int width, const QVector& contents, const QVector& materials); @@ -576,18 +572,15 @@ public: HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material, const QColor& color); - void getRangeAfterHeightPaint(const glm::vec3& position, const glm::vec3& radius, - float height, int& minimum, int& maximum) const; + void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const; - HeightfieldNode* paintHeight(const glm::vec3& position, const glm::vec3& radius, float height, - float normalizeScale, float normalizeOffset); - - HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& bounds, SharedObjectPointer& heightfield); + HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius, float height, float normalizeScale, float normalizeOffset); - void getRangeAfterMaterialSet(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const Box& spannerBounds, int& minimum, int& maximum) const; - + 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, float normalizeScale, float normalizeOffset); @@ -614,6 +607,9 @@ private: QRgb getColorAt(const glm::vec3& location) const; int getMaterialAt(const glm::vec3& location) const; + void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth, + QVector& heightContents, QVector& stackContents); + HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; @@ -683,8 +679,6 @@ public: virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); - virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield); - virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color); @@ -724,6 +718,8 @@ private slots: private: + Heightfield* prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset); + float _aspectY; float _aspectZ; From 8629f7d00a83cfac93a9e0944bf8d35dad4e3f6b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 17 Dec 2014 20:08:15 -0800 Subject: [PATCH 12/67] Fiddly normalization bits. --- libraries/metavoxels/src/Spanner.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index db0517e83d..0c486281b8 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1826,6 +1826,8 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const return newNode; } +const float HERMITE_GRANULARITY = 1.0f / numeric_limits::max(); + void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const { if (!_height) { @@ -1883,6 +1885,14 @@ void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, con } lineDest += heightWidth; } + + // make sure we increment in multiples of the voxel size + float voxelStep = scale.x / innerHeightWidth; + float newScaleY = (maximum - minimum + 1.0f) * scale.y / numeric_limits::max(); + float newSteps = newScaleY / voxelStep; + if (glm::abs(newSteps - glm::round(newSteps)) > HERMITE_GRANULARITY) { + minimum -= (voxelStep * glm::ceil(newSteps) - newScaleY) * numeric_limits::max() / scale.y; + } } HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, @@ -2010,6 +2020,14 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: minimum = qMin(minimum, start.y); maximum = qMax(maximum, end.y); + + // make sure we increment in multiples of the voxel size + float voxelStep = scale.x / innerHeightWidth; + float newScaleY = (maximum - minimum + 1.0f) * scale.y / numeric_limits::max(); + float newSteps = newScaleY / voxelStep; + if (glm::abs(newSteps - glm::round(newSteps)) > HERMITE_GRANULARITY) { + minimum -= (voxelStep * glm::ceil(newSteps) - newScaleY) * numeric_limits::max() / scale.y; + } } HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, From c7cae28c07aa821d5a2c9d88e6d147537d0b4580 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 17 Dec 2014 20:38:10 -0800 Subject: [PATCH 13/67] Increment fix. --- libraries/metavoxels/src/Spanner.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 0c486281b8..76aab6ad82 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1888,10 +1888,10 @@ void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, con // make sure we increment in multiples of the voxel size float voxelStep = scale.x / innerHeightWidth; - float newScaleY = (maximum - minimum + 1.0f) * scale.y / numeric_limits::max(); - float newSteps = newScaleY / voxelStep; - if (glm::abs(newSteps - glm::round(newSteps)) > HERMITE_GRANULARITY) { - minimum -= (voxelStep * glm::ceil(newSteps) - newScaleY) * numeric_limits::max() / scale.y; + float heightIncrement = (1.0f - minimum) * scale.y / numeric_limits::max(); + float incrementSteps = heightIncrement / voxelStep; + if (glm::abs(incrementSteps - glm::round(incrementSteps)) > HERMITE_GRANULARITY) { + minimum = 1.0f - voxelStep * glm::ceil(incrementSteps) * numeric_limits::max() / scale.y; } } @@ -2023,10 +2023,10 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: // make sure we increment in multiples of the voxel size float voxelStep = scale.x / innerHeightWidth; - float newScaleY = (maximum - minimum + 1.0f) * scale.y / numeric_limits::max(); - float newSteps = newScaleY / voxelStep; - if (glm::abs(newSteps - glm::round(newSteps)) > HERMITE_GRANULARITY) { - minimum -= (voxelStep * glm::ceil(newSteps) - newScaleY) * numeric_limits::max() / scale.y; + float heightIncrement = (1.0f - minimum) * scale.y / numeric_limits::max(); + float incrementSteps = heightIncrement / voxelStep; + if (glm::abs(incrementSteps - glm::round(incrementSteps)) > HERMITE_GRANULARITY) { + minimum = 1.0f - voxelStep * glm::ceil(incrementSteps) * numeric_limits::max() / scale.y; } } From f299004e09ac7dc03c122ce0c97abdad488b7895 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 18 Dec 2014 16:26:33 -0800 Subject: [PATCH 14/67] The journey continues and continues and continues. --- libraries/metavoxels/src/Spanner.cpp | 128 +++++++++++++++++++-------- libraries/metavoxels/src/Spanner.h | 1 + 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 76aab6ad82..eb8d64e728 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1156,8 +1156,10 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, const int HeightfieldStack::POSITION_BYTES = sizeof(quint16); +const int HeightfieldStack::COLOR_MATERIAL_BYTES = 4 + 1; + // RGBA color, material, Hermite values for X, Y, and Z -const int HeightfieldStack::ENTRY_BYTES = 4 + 1 + 4 * 3; +const int HeightfieldStack::ENTRY_BYTES = COLOR_MATERIAL_BYTES + 4 * 3; HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : @@ -2163,28 +2165,79 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float distance; glm::vec3 normal; + float colorX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerColorWidth / innerHeightWidth; + float colorZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerColorHeight / innerHeightHeight; + + float materialX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialWidth / innerHeightWidth; + float materialZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialHeight / innerHeightHeight; + float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { QByteArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; - quint16 oldHeight = *heightLineDest; - int offset = 0; - if (oldHeight != 0 && !stackDest->isEmpty()) { - int oldTop = oldHeight * voxelScale; - offset = qMax(0, oldTop - newTop) * HeightfieldStack::ENTRY_BYTES; - int prepend = qMax(0, newTop - oldTop) * HeightfieldStack::ENTRY_BYTES; - int append = qMax(0, (oldTop - stackDest->size() + 1) - newBottom) * HeightfieldStack::ENTRY_BYTES; + if (stackDest->isEmpty() && *heightLineDest != 0) { + // initialize from heightfield + *stackDest = QByteArray(HeightfieldStack::POSITION_BYTES + HeightfieldStack::ENTRY_BYTES, 0); + float voxelHeight = *heightLineDest * voxelScale; + *(quint16*)stackDest->data() = glm::floor(voxelHeight); + char* entryDest = stackDest->data() + HeightfieldStack::POSITION_BYTES; + + if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { + const char* colorDest = newColorContents.constData() + ((int)colorZ * colorWidth + + (int)colorX) * DataBlock::COLOR_BYTES; + entryDest[0] = colorDest[0]; + entryDest[1] = colorDest[1]; + entryDest[2] = colorDest[2]; + } + entryDest[3] = 0xFF; + + if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && + materialZ <= innerMaterialHeight) { + const uchar* materialDest = (const uchar*)newMaterialContents.constData() + + (int)materialZ * materialWidth + (int)materialX; + int index = *materialDest; + if (index != 0) { + int& mapping = materialMappings[index]; + if (mapping == 0) { + QByteArray dummyContents; + mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), + newStackMaterials, dummyContents); + } + index = mapping; + } + entryDest[4] = index; + } + + entryDest[12] = glm::fract(voxelHeight) * numeric_limits::max(); + + } + if (!stackDest->isEmpty()) { + int oldBottom = *(const quint16*)stackDest->constData(); + int oldTop = oldBottom + (stackDest->size() - HeightfieldStack::POSITION_BYTES) / + HeightfieldStack::ENTRY_BYTES - 1; + int prepend = qMax(0, oldBottom - newBottom) * HeightfieldStack::ENTRY_BYTES; + int append = qMax(0, newTop - oldTop) * HeightfieldStack::ENTRY_BYTES; if (prepend != 0 || append != 0) { QByteArray newStack(prepend + stackDest->size() + append, 0); - memcpy(newStack.data() + prepend, stackDest->constData(), stackDest->size()); + memcpy(newStack.data() + HeightfieldStack::POSITION_BYTES + prepend, stackDest->constData() + + HeightfieldStack::POSITION_BYTES, stackDest->size() - HeightfieldStack::POSITION_BYTES); + for (char* entryDest = newStack.data() + HeightfieldStack::POSITION_BYTES, *end = entryDest + prepend; + entryDest != end; entryDest += HeightfieldStack::ENTRY_BYTES) { + memcpy(entryDest, end, HeightfieldStack::COLOR_MATERIAL_BYTES); + } *stackDest = newStack; + *(quint16*)stackDest->data() = qMin(oldBottom, newBottom); } } else { - *stackDest = QByteArray((newTop - newBottom + 1) * HeightfieldStack::ENTRY_BYTES, 0); + *stackDest = QByteArray(HeightfieldStack::POSITION_BYTES + (newTop - newBottom + 1) * + HeightfieldStack::ENTRY_BYTES, 0); + *(quint16*)stackDest->data() = newBottom; } - char* entryDest = stackDest->data() + offset; + quint16& stackPos = *(quint16*)stackDest->data(); + char* entryDest = stackDest->data() + HeightfieldStack::POSITION_BYTES + + (newTop - stackPos) * HeightfieldStack::ENTRY_BYTES; glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; - for (int y = newTop; y >= newBottom; y--, entryDest += HeightfieldStack::ENTRY_BYTES, pos -= voxelStepY) { + for (int y = newTop; y >= newBottom; y--, entryDest -= HeightfieldStack::ENTRY_BYTES, pos -= voxelStepY) { if (spanner->contains(pos)) { if (hasOwnColors && !erase) { QRgb spannerColor = spanner->getColorAt(pos); @@ -2215,39 +2268,40 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest[4] = materialIndex; } } + bool currentSet = (entryDest[3] != 0); int nextStackX = (int)stackX + 1; if (nextStackX <= innerStackWidth) { - QByteArray* nextStackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)nextStackX; + const QByteArray* nextStackDest = newStackContents.constData() + + (int)stackZ * stackWidth + (int)nextStackX; } - if (entryDest != stackDest->data()) { - bool previousSet = ((entryDest - HeightfieldStack::ENTRY_BYTES)[3] != 0); - bool currentSet = (entryDest[3] != 0); - if (previousSet == currentSet) { - entryDest[9] = entryDest[10] = entryDest[11] = entryDest[12] = 0; + bool nextSetY = (entryDest != stackDest->data() + stackDest->size() - HeightfieldStack::ENTRY_BYTES && + (entryDest + HeightfieldStack::ENTRY_BYTES)[3] != 0); + if (nextSetY == currentSet) { + entryDest[9] = entryDest[10] = entryDest[11] = entryDest[12] = 0; + } else { + bool intersects; + if (erase == nextSetY) { + if ((intersects = spanner->intersects(pos - voxelStepY, pos, distance, normal))) { + distance = 1.0f - distance; + } } else { - bool intersects; - if (erase == previousSet) { - if ((intersects = spanner->intersects(pos - voxelStepY, pos, distance, normal))) { - distance = 1.0f - distance; - } - } else { - intersects = spanner->intersects(pos, pos - voxelStepY, distance, normal); - } - if (intersects) { - if (erase) { - normal = -normal; - } - entryDest[9] = normal.x * numeric_limits::max(); - entryDest[10] = normal.y * numeric_limits::max(); - entryDest[11] = normal.z * numeric_limits::max(); - entryDest[12] = distance * numeric_limits::max(); + intersects = spanner->intersects(pos, pos - voxelStepY, distance, normal); + } + if (intersects) { + if (erase) { + normal = -normal; } + entryDest[9] = normal.x * numeric_limits::max(); + entryDest[10] = normal.y * numeric_limits::max(); + entryDest[11] = normal.z * numeric_limits::max(); + entryDest[12] = distance * numeric_limits::max(); } } int nextStackZ = (int)stackZ + 1; if (nextStackZ <= innerStackHeight) { - QByteArray* nextStackDest = newStackContents.data() + (int)nextStackZ * stackWidth + (int)stackX; + const QByteArray* nextStackDest = newStackContents.constData() + + (int)nextStackZ * stackWidth + (int)stackX; } } @@ -2265,8 +2319,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (height >= *heightLineDest) { *heightLineDest = height; - float colorX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerColorWidth / innerHeightWidth; - float colorZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerColorHeight / innerHeightHeight; if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { char* colorDest = newColorContents.data() + ((int)colorZ * colorWidth + (int)colorX) * DataBlock::COLOR_BYTES; @@ -2283,8 +2335,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } } - float materialX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialWidth / innerHeightWidth; - float materialZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialHeight / innerHeightHeight; if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && materialZ <= innerMaterialHeight) { char* materialDest = newMaterialContents.data() + (int)materialZ * materialWidth + (int)materialX; diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 48e17161e4..811910a042 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -474,6 +474,7 @@ class HeightfieldStack : public HeightfieldData { public: static const int POSITION_BYTES; + static const int COLOR_MATERIAL_BYTES; static const int ENTRY_BYTES; HeightfieldStack(int width, const QVector& contents, const QVector& materials); From 377dc23dbe4a835337e381355bb7ed76e3043bf4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 18 Dec 2014 18:23:24 -0800 Subject: [PATCH 15/67] Wrap the QByteArray into a more convenient subclass. --- libraries/metavoxels/src/Spanner.cpp | 172 ++++++++++++--------------- libraries/metavoxels/src/Spanner.h | 57 +++++++-- 2 files changed, 128 insertions(+), 101 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index eb8d64e728..c52fc449ff 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1116,23 +1116,22 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, int height, - const QVector& contents) { + const QVector& contents) { QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; *header++ = width; *header++ = height; - foreach (const QByteArray& stack, contents) { - quint16 entries = stack.isEmpty() ? 0 : (stack.size() - HeightfieldStack::POSITION_BYTES) / - HeightfieldStack::ENTRY_BYTES; + foreach (const StackArray& stack, contents) { + quint16 entries = stack.getEntryCount(); inflated.append((const char*)&entries, sizeof(quint16)); inflated.append(stack); } return qCompress(inflated); } -static QVector decodeHeightfieldStack(const QByteArray& encoded, +static QVector decodeHeightfieldStack(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { QByteArray inflated = qUncompress(encoded); const qint32* header = (const qint32*)inflated.constData(); @@ -1141,27 +1140,27 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, width = *header++; height = *header++; const char* src = inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; - QVector contents(width * height); - for (QByteArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { + QVector contents(width * height); + for (StackArray* dest = contents.data(), *end = dest + contents.size(); dest != end; dest++) { int entries = *(const quint16*)src; src += sizeof(quint16); if (entries > 0) { - int bytes = HeightfieldStack::POSITION_BYTES + entries * HeightfieldStack::ENTRY_BYTES; - *dest = QByteArray(src, bytes); + int bytes = StackArray::getSize(entries); + *dest = StackArray(src, bytes); src += bytes; } } return contents; } -const int HeightfieldStack::POSITION_BYTES = sizeof(quint16); +void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { + hermiteY[0] = position * numeric_limits::max(); + hermiteY[1] = position * numeric_limits::max(); + hermiteY[2] = position * numeric_limits::max(); + hermiteY[3] = position * numeric_limits::max(); +} -const int HeightfieldStack::COLOR_MATERIAL_BYTES = 4 + 1; - -// RGBA color, material, Hermite values for X, Y, and Z -const int HeightfieldStack::ENTRY_BYTES = COLOR_MATERIAL_BYTES + 4 * 3; - -HeightfieldStack::HeightfieldStack(int width, const QVector& contents, +HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), _contents(contents), @@ -1185,7 +1184,7 @@ HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldSt _contents = reference->getContents(); int offsetX, offsetY, width, height; - QVector delta = decodeHeightfieldStack(reference->getEncodedDelta(), offsetX, offsetY, width, height); + QVector delta = decodeHeightfieldStack(reference->getEncodedDelta(), offsetX, offsetY, width, height); if (delta.isEmpty()) { return; } @@ -1196,11 +1195,11 @@ HeightfieldStack::HeightfieldStack(Bitstream& in, int bytes, const HeightfieldSt } int minX = offsetX - 1; int minY = offsetY - 1; - const QByteArray* src = delta.constData(); - QByteArray* dest = _contents.data() + minY * _width + minX; + const StackArray* src = delta.constData(); + StackArray* dest = _contents.data() + minY * _width + minX; for (int y = 0; y < height; y++, src += width, dest += _width) { - const QByteArray* lineSrc = src; - for (QByteArray* lineDest = dest, *end = dest + width; lineDest != end; lineDest++, lineSrc++) { + const StackArray* lineSrc = src; + for (StackArray* lineDest = dest, *end = dest + width; lineDest != end; lineDest++, lineSrc++) { *lineDest = *lineSrc; } } @@ -1230,8 +1229,8 @@ void HeightfieldStack::writeDelta(Bitstream& out, const HeightfieldStackPointer& int height = _contents.size() / _width; int minX = _width, minY = height; int maxX = -1, maxY = -1; - const QByteArray* src = _contents.constData(); - const QByteArray* ref = reference->getContents().constData(); + const StackArray* src = _contents.constData(); + const StackArray* ref = reference->getContents().constData(); for (int y = 0; y < height; y++) { bool difference = false; for (int x = 0; x < _width; x++) { @@ -1246,17 +1245,17 @@ void HeightfieldStack::writeDelta(Bitstream& out, const HeightfieldStackPointer& maxY = qMax(maxY, y); } } - QVector delta; + QVector delta; int deltaWidth = 0, deltaHeight = 0; if (maxX >= minX) { deltaWidth = maxX - minX + 1; deltaHeight = maxY - minY + 1; - delta = QVector(deltaWidth * deltaHeight); - QByteArray* dest = delta.data(); + delta = QVector(deltaWidth * deltaHeight); + StackArray* dest = delta.data(); src = _contents.constData() + minY * _width + minX; for (int y = 0; y < deltaHeight; y++, src += _width, dest += deltaWidth) { - const QByteArray* lineSrc = src; - for (QByteArray* lineDest = dest, *end = dest + deltaWidth; lineDest != end; lineDest++, lineSrc++) { + const StackArray* lineSrc = src; + for (StackArray* lineDest = dest, *end = dest + deltaWidth; lineDest != end; lineDest++, lineSrc++) { *lineDest = *lineSrc; } } @@ -1941,7 +1940,7 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons QVector newHeightContents = _height->getContents(); int stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; - QVector newStackContents; + QVector newStackContents; QVector newStackMaterials; if (_stack) { stackWidth = _stack->getWidth(); @@ -2072,7 +2071,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons QVector newHeightContents = _height->getContents(); int stackWidth, stackHeight; - QVector newStackContents; + QVector newStackContents; QVector newStackMaterials; if (_stack) { stackWidth = _stack->getWidth(); @@ -2083,7 +2082,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } else { stackWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; stackHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; - newStackContents = QVector(stackWidth * stackHeight); + newStackContents = QVector(stackWidth * stackHeight); } int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; @@ -2152,7 +2151,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int newBottom = end.y * voxelScale; char r = color.red(), g = color.green(), b = color.blue(), a = color.alpha(); bool erase = (a == 0); - uchar materialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); + uchar materialMaterialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); + QByteArray dummyContents; + uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, dummyContents); bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); QHash materialMappings; @@ -2174,23 +2175,20 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { - QByteArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; + StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; if (stackDest->isEmpty() && *heightLineDest != 0) { // initialize from heightfield - *stackDest = QByteArray(HeightfieldStack::POSITION_BYTES + HeightfieldStack::ENTRY_BYTES, 0); + *stackDest = StackArray(1); float voxelHeight = *heightLineDest * voxelScale; - *(quint16*)stackDest->data() = glm::floor(voxelHeight); - char* entryDest = stackDest->data() + HeightfieldStack::POSITION_BYTES; - - if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { - const char* colorDest = newColorContents.constData() + ((int)colorZ * colorWidth + - (int)colorX) * DataBlock::COLOR_BYTES; - entryDest[0] = colorDest[0]; - entryDest[1] = colorDest[1]; - entryDest[2] = colorDest[2]; - } - entryDest[3] = 0xFF; + stackDest->setPosition(glm::floor(voxelHeight)); + StackArray::Entry* entryDest = stackDest->getEntryData(); + if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { + const uchar* colorDest = (const uchar*)newColorContents.constData() + ((int)colorZ * colorWidth + + (int)colorX) * DataBlock::COLOR_BYTES; + entryDest->setRGB(colorDest); + } + if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && materialZ <= innerMaterialHeight) { const uchar* materialDest = (const uchar*)newMaterialContents.constData() + @@ -2199,58 +2197,48 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (index != 0) { int& mapping = materialMappings[index]; if (mapping == 0) { - QByteArray dummyContents; mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), newStackMaterials, dummyContents); } index = mapping; } - entryDest[4] = index; + entryDest->material = index; } - entryDest[12] = glm::fract(voxelHeight) * numeric_limits::max(); + entryDest->hermiteY[0] = glm::fract(voxelHeight) * numeric_limits::max(); } if (!stackDest->isEmpty()) { - int oldBottom = *(const quint16*)stackDest->constData(); - int oldTop = oldBottom + (stackDest->size() - HeightfieldStack::POSITION_BYTES) / - HeightfieldStack::ENTRY_BYTES - 1; - int prepend = qMax(0, oldBottom - newBottom) * HeightfieldStack::ENTRY_BYTES; - int append = qMax(0, newTop - oldTop) * HeightfieldStack::ENTRY_BYTES; + int oldBottom = stackDest->getPosition(); + int oldTop = oldBottom + stackDest->getEntryCount() - 1; + int prepend = qMax(0, oldBottom - newBottom); + int append = qMax(0, newTop - oldTop); if (prepend != 0 || append != 0) { - QByteArray newStack(prepend + stackDest->size() + append, 0); - memcpy(newStack.data() + HeightfieldStack::POSITION_BYTES + prepend, stackDest->constData() + - HeightfieldStack::POSITION_BYTES, stackDest->size() - HeightfieldStack::POSITION_BYTES); - for (char* entryDest = newStack.data() + HeightfieldStack::POSITION_BYTES, *end = entryDest + prepend; - entryDest != end; entryDest += HeightfieldStack::ENTRY_BYTES) { - memcpy(entryDest, end, HeightfieldStack::COLOR_MATERIAL_BYTES); + StackArray newStack(prepend + stackDest->getEntryCount() + append); + memcpy(newStack.getEntryData() + prepend, stackDest->getEntryData(), + stackDest->getEntryCount() * sizeof(StackArray::Entry)); + for (StackArray::Entry* entryDest = newStack.getEntryData(), *end = entryDest + prepend; + entryDest != end; entryDest++) { + entryDest->setRGBA(end->rgba); + entryDest->material = end->material; } *stackDest = newStack; - *(quint16*)stackDest->data() = qMin(oldBottom, newBottom); + stackDest->setPosition(qMin(oldBottom, newBottom)); } } else { - *stackDest = QByteArray(HeightfieldStack::POSITION_BYTES + (newTop - newBottom + 1) * - HeightfieldStack::ENTRY_BYTES, 0); - *(quint16*)stackDest->data() = newBottom; + *stackDest = StackArray(newTop - newBottom + 1); + stackDest->setPosition(newBottom); } - quint16& stackPos = *(quint16*)stackDest->data(); - char* entryDest = stackDest->data() + HeightfieldStack::POSITION_BYTES + - (newTop - stackPos) * HeightfieldStack::ENTRY_BYTES; + quint16& stackPos = stackDest->getPositionRef(); + StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackPos); glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; - for (int y = newTop; y >= newBottom; y--, entryDest -= HeightfieldStack::ENTRY_BYTES, pos -= voxelStepY) { + for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= voxelStepY) { if (spanner->contains(pos)) { if (hasOwnColors && !erase) { - QRgb spannerColor = spanner->getColorAt(pos); - entryDest[0] = qRed(spannerColor); - entryDest[1] = qGreen(spannerColor); - entryDest[2] = qBlue(spannerColor); - entryDest[3] = qAlpha(spannerColor); + entryDest->setRGBA(spanner->getColorAt(pos)); } else { - entryDest[0] = r; - entryDest[1] = g; - entryDest[2] = b; - entryDest[3] = a; + entryDest->setRGBA(r, g, b, a); } if (hasOwnMaterials && !erase) { int index = spanner->getMaterialAt(pos); @@ -2258,27 +2246,28 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int& mapping = materialMappings[index]; if (mapping == 0) { mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), - newMaterialMaterials, newMaterialContents); + newStackMaterials, dummyContents); } index = mapping; } - entryDest[4] = index; + entryDest->material = index; } else { - entryDest[4] = materialIndex; + entryDest->material = stackMaterialIndex; } } - bool currentSet = (entryDest[3] != 0); + bool currentSet = entryDest->isSet(); int nextStackX = (int)stackX + 1; if (nextStackX <= innerStackWidth) { - const QByteArray* nextStackDest = newStackContents.constData() + + const StackArray* nextStackDest = newStackContents.constData() + (int)stackZ * stackWidth + (int)nextStackX; } - bool nextSetY = (entryDest != stackDest->data() + stackDest->size() - HeightfieldStack::ENTRY_BYTES && - (entryDest + HeightfieldStack::ENTRY_BYTES)[3] != 0); + bool nextSetY = (entryDest != stackDest->getEntryData() + stackDest->getEntryCount() - 1 && + (entryDest + 1)->isSet()); if (nextSetY == currentSet) { - entryDest[9] = entryDest[10] = entryDest[11] = entryDest[12] = 0; + entryDest->clearHermiteY(); + } else { bool intersects; if (erase == nextSetY) { @@ -2292,15 +2281,12 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (erase) { normal = -normal; } - entryDest[9] = normal.x * numeric_limits::max(); - entryDest[10] = normal.y * numeric_limits::max(); - entryDest[11] = normal.z * numeric_limits::max(); - entryDest[12] = distance * numeric_limits::max(); + entryDest->setHermiteY(normal, distance); } } int nextStackZ = (int)stackZ + 1; if (nextStackZ <= innerStackHeight) { - const QByteArray* nextStackDest = newStackContents.constData() + + const StackArray* nextStackDest = newStackContents.constData() + (int)nextStackZ * stackWidth + (int)stackX; } @@ -2351,7 +2337,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons *materialDest = index; } else { - *materialDest = materialIndex; + *materialDest = materialMaterialIndex; } } } @@ -2828,7 +2814,7 @@ int HeightfieldNode::getMaterialAt(const glm::vec3& location) const { } void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, - int innerStackWidth, QVector& heightContents, QVector& stackContents) { + int innerStackWidth, QVector& heightContents, QVector& stackContents) { if (normalizeScale == 1.0f && normalizeOffset == 0.0f) { return; } @@ -2843,9 +2829,9 @@ void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeSc } int stackOffset = glm::round(scale.y * normalizeOffset * normalizeScale * innerStackWidth / (numeric_limits::max() * scale.x)); - for (QByteArray* dest = stackContents.data(), *end = dest + stackContents.size(); dest != end; dest++) { + for (StackArray* dest = stackContents.data(), *end = dest + stackContents.size(); dest != end; dest++) { if (!dest->isEmpty()) { - *(quint16*)dest->data() += stackOffset; + dest->getPositionRef() += stackOffset; } } } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 811910a042..8e4d141b02 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -469,19 +469,60 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const typedef QExplicitlySharedDataPointer HeightfieldStackPointer; +/// A single column within a stack block. +class StackArray : public QByteArray { +public: + +#pragma pack(push, 1) + /// A single entry within the array. + class Entry { + public: + uchar rgba[4]; + uchar material; + uchar hermiteX[4]; + uchar hermiteY[4]; + uchar hermiteZ[4]; + + void setRGBA(int r, int g, int b, int a = 0xFF) { rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a; } + void setRGBA(const uchar* orgba) { setRGBA(orgba[0], orgba[1], orgba[2], orgba[3]); } + void setRGB(const uchar* rgb) { setRGBA(rgb[0], rgb[1], rgb[2]); } + void setRGBA(QRgb rgb) { setRGBA(qRed(rgb), qGreen(rgb), qBlue(rgb), qAlpha(rgb)); } + + bool isSet() const { return rgba[3] != 0; } + + void setHermiteY(int x, int y, int z, int w) { hermiteY[0] = x; hermiteY[1] = y; hermiteY[2] = z; hermiteY[3] = w; } + void setHermiteY(const glm::vec3& normal, float position); + void clearHermiteY() { setHermiteY(0, 0, 0, 0); } + }; +#pragma pack(pop) + + static int getSize(int entries) { return (entries == 0) ? 0 : sizeof(quint16) + sizeof(Entry) * entries; } + + StackArray() : QByteArray() { } + StackArray(int entries) : QByteArray(getSize(entries), 0) { } + StackArray(const QByteArray& other) : QByteArray(other) { } + StackArray(const char* src, int bytes) : QByteArray(src, bytes) { } + + int getPosition() const { return *(const quint16*)constData(); } + void setPosition(int position) { *(quint16*)data() = position; } + + quint16& getPositionRef() { return *(quint16*)data(); } + + int getEntryCount() const { return isEmpty() ? 0 : (size() - sizeof(quint16)) / sizeof(Entry); } + + Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } + const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } +}; + /// A block of stack data associated with a heightfield. class HeightfieldStack : public HeightfieldData { public: - static const int POSITION_BYTES; - static const int COLOR_MATERIAL_BYTES; - static const int ENTRY_BYTES; - - HeightfieldStack(int width, const QVector& contents, const QVector& materials); + HeightfieldStack(int width, const QVector& contents, const QVector& materials); HeightfieldStack(Bitstream& in, int bytes); HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference); - QVector& getContents() { return _contents; } + QVector& getContents() { return _contents; } QVector& getMaterials() { return _materials; } void write(Bitstream& out); @@ -491,7 +532,7 @@ private: void read(Bitstream& in, int bytes); - QVector _contents; + QVector _contents; QVector _materials; }; @@ -609,7 +650,7 @@ private: int getMaterialAt(const glm::vec3& location) const; void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth, - QVector& heightContents, QVector& stackContents); + QVector& heightContents, QVector& stackContents); HeightfieldHeightPointer _height; HeightfieldColorPointer _color; From 2711b3f3128d17f1c6c6bfe7f5be67852ac628b1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 19 Dec 2014 15:37:58 -0800 Subject: [PATCH 16/67] Closer to working edits. --- interface/src/MetavoxelSystem.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 109 +++++++++++++++++++++------ libraries/metavoxels/src/Spanner.h | 5 ++ 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 6ad72d835b..d1124c7767 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2206,7 +2206,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g quint16* lineDest = dest; for (int x = 0; x < stackWidth; x++, src++, lineDest++) { if (!src->isEmpty()) { - *lineDest = 0; + //*lineDest = 0; } } } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index c52fc449ff..a32c51db0c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1153,10 +1153,23 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, return contents; } +static inline bool isZero(const uchar values[4]) { + return *(const quint32*)values == 0; +} + +bool StackArray::Entry::isZero() const { + return ::isZero(rgba) && material == 0 && ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); +} + +bool StackArray::Entry::isMergeable(const Entry& other) const { + return *(const quint32*)rgba == *(const quint32*)other.rgba && material == other.material && + ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); +} + void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { - hermiteY[0] = position * numeric_limits::max(); - hermiteY[1] = position * numeric_limits::max(); - hermiteY[2] = position * numeric_limits::max(); + hermiteY[0] = normal.x * numeric_limits::max(); + hermiteY[1] = normal.y * numeric_limits::max(); + hermiteY[2] = normal.z * numeric_limits::max(); hermiteY[3] = position * numeric_limits::max(); } @@ -2168,12 +2181,22 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float colorX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerColorWidth / innerHeightWidth; float colorZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerColorHeight / innerHeightHeight; - + uchar* colorDest = (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) ? + ((uchar*)newColorContents.data() + ((int)colorZ * colorWidth + (int)colorX) * DataBlock::COLOR_BYTES) : NULL; + float materialX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialWidth / innerHeightWidth; float materialZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerMaterialHeight / innerHeightHeight; - + char* materialDest = (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && + materialZ <= innerMaterialHeight) ? (newMaterialContents.data() + + (int)materialZ * materialWidth + (int)materialX) : NULL; + float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; + + int topHeight = -1; + char topR = 0, topG = 0, topB = 0; + uchar topMaterial; + if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; if (stackDest->isEmpty() && *heightLineDest != 0) { @@ -2183,16 +2206,11 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->setPosition(glm::floor(voxelHeight)); StackArray::Entry* entryDest = stackDest->getEntryData(); - if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { - const uchar* colorDest = (const uchar*)newColorContents.constData() + ((int)colorZ * colorWidth + - (int)colorX) * DataBlock::COLOR_BYTES; + if (colorDest) { entryDest->setRGB(colorDest); } - if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && - materialZ <= innerMaterialHeight) { - const uchar* materialDest = (const uchar*)newMaterialContents.constData() + - (int)materialZ * materialWidth + (int)materialX; + if (materialDest) { int index = *materialDest; if (index != 0) { int& mapping = materialMappings[index]; @@ -2229,8 +2247,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons *stackDest = StackArray(newTop - newBottom + 1); stackDest->setPosition(newBottom); } - quint16& stackPos = stackDest->getPositionRef(); - StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackPos); + StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= voxelStepY) { if (spanner->contains(pos)) { @@ -2271,11 +2288,11 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } else { bool intersects; if (erase == nextSetY) { - if ((intersects = spanner->intersects(pos - voxelStepY, pos, distance, normal))) { + if ((intersects = spanner->intersects(pos + voxelStepY, pos, distance, normal))) { distance = 1.0f - distance; } } else { - intersects = spanner->intersects(pos, pos - voxelStepY, distance, normal); + intersects = spanner->intersects(pos, pos + voxelStepY, distance, normal); } if (intersects) { if (erase) { @@ -2291,9 +2308,59 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } } + + // prune zero entries from end, repeated entries from beginning + int endPruneCount = 0; + for (int i = stackDest->getEntryCount() - 1; i >= 0 && stackDest->getEntryData()[i].isZero(); i--) { + endPruneCount++; + } + if (endPruneCount == stackDest->getEntryCount()) { + topHeight = 0; + stackDest->clear(); + + } else { + int topIndex = stackDest->getEntryCount() - 1; + while (topIndex > 0 && !stackDest->getEntryData()[topIndex].isSet()) { + topIndex--; + } + StackArray::Entry* topEntry = stackDest->getEntryData() + topIndex; + if (topEntry->isSet()) { + topHeight = ((stackDest->getPosition() + topIndex) + + topEntry->hermiteY[3] / (float)numeric_limits::max()) / voxelScale; + topR = topEntry->rgba[0]; + topG = topEntry->rgba[1]; + topB = topEntry->rgba[2]; + topMaterial = topEntry->material; + + } else { + topHeight = 0; + } + stackDest->removeEntries(stackDest->getEntryCount() - endPruneCount, endPruneCount); + int beginningPruneCount = 0; + for (int i = 0; i < stackDest->getEntryCount() - 1 && stackDest->getEntryData()[i].isMergeable( + stackDest->getEntryData()[i + 1]); i++) { + beginningPruneCount++; + } + stackDest->removeEntries(0, beginningPruneCount); + stackDest->getPositionRef() += beginningPruneCount; + } } - if (erase) { + if (topHeight != -1) { + *heightLineDest = topHeight; + if (colorDest) { + colorDest[0] = topR; + colorDest[1] = topG; + colorDest[2] = topB; + } + if (materialDest) { + if (topMaterial != 0) { + topMaterial = getMaterialIndex(newStackMaterials.at(topMaterial - 1), + newMaterialMaterials, newMaterialContents); + } + *materialDest = topMaterial; + } + } else if (erase) { if (spanner->intersects(endPos, worldPos, distance, normal)) { quint16 height = glm::round(glm::mix(end.y, start.y, distance)); if (height <= *heightLineDest) { @@ -2305,9 +2372,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (height >= *heightLineDest) { *heightLineDest = height; - if (colorX >= 0.0f && colorX <= innerColorWidth && colorZ >= 0.0f && colorZ <= innerColorHeight) { - char* colorDest = newColorContents.data() + ((int)colorZ * colorWidth + - (int)colorX) * DataBlock::COLOR_BYTES; + if (colorDest) { if (hasOwnColors) { QRgb spannerColor = spanner->getColorAt(glm::mix(endPos, worldPos, distance)); colorDest[0] = qRed(spannerColor); @@ -2321,9 +2386,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } } - if (materialX >= 0.0f && materialX <= innerMaterialWidth && materialZ >= 0.0f && - materialZ <= innerMaterialHeight) { - char* materialDest = newMaterialContents.data() + (int)materialZ * materialWidth + (int)materialX; + if (materialDest) { if (hasOwnMaterials) { int index = spanner->getMaterialAt(glm::mix(endPos, worldPos, distance)); if (index != 0) { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 8e4d141b02..729fd72efa 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -490,6 +490,9 @@ public: bool isSet() const { return rgba[3] != 0; } + bool isZero() const; + bool isMergeable(const Entry& other) const; + void setHermiteY(int x, int y, int z, int w) { hermiteY[0] = x; hermiteY[1] = y; hermiteY[2] = z; hermiteY[3] = w; } void setHermiteY(const glm::vec3& normal, float position); void clearHermiteY() { setHermiteY(0, 0, 0, 0); } @@ -512,6 +515,8 @@ public: Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } + + void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } }; /// A block of stack data associated with a heightfield. From a563c66ab8e67724e2ef58f17dd7f652876926c5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 19 Dec 2014 17:16:45 -0800 Subject: [PATCH 17/67] Neighbor Hermite bits. --- libraries/metavoxels/src/Spanner.cpp | 91 ++++++++++++++++++++++------ libraries/metavoxels/src/Spanner.h | 8 +++ 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index a32c51db0c..ecd1a41fd1 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1166,6 +1166,13 @@ bool StackArray::Entry::isMergeable(const Entry& other) const { ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); } +void StackArray::Entry::setHermiteX(const glm::vec3& normal, float position) { + hermiteX[0] = normal.x * numeric_limits::max(); + hermiteX[1] = normal.y * numeric_limits::max(); + hermiteX[2] = normal.z * numeric_limits::max(); + hermiteX[3] = position * numeric_limits::max(); +} + void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { hermiteY[0] = normal.x * numeric_limits::max(); hermiteY[1] = normal.y * numeric_limits::max(); @@ -1173,6 +1180,13 @@ void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { hermiteY[3] = position * numeric_limits::max(); } +void StackArray::Entry::setHermiteZ(const glm::vec3& normal, float position) { + hermiteZ[0] = normal.x * numeric_limits::max(); + hermiteZ[1] = normal.y * numeric_limits::max(); + hermiteZ[2] = normal.z * numeric_limits::max(); + hermiteZ[3] = position * numeric_limits::max(); +} + HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), @@ -2044,6 +2058,15 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: } } +static inline bool isSet(const StackArray& array, int y) { + return !array.isEmpty() && y >= array.getPosition() && y < array.getPosition() + array.getEntryCount() && + array.getEntryData()[y - array.getPosition()].isSet(); +} + +static inline bool isSet(const QVector& stackContents, int stackWidth, int x, int y, int z) { + return isSet(stackContents.at(z * stackWidth + x), y); +} + HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, Spanner* spanner, const SharedObjectPointer& material, const QColor& color, float normalizeScale, float normalizeOffset) { @@ -2106,6 +2129,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } + QVector oldStackContents = newStackContents; int colorWidth, colorHeight; QByteArray newColorContents; @@ -2159,7 +2183,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); float voxelStep = scale.x / innerHeightWidth; float voxelScale = scale.y / (numeric_limits::max() * voxelStep); + glm::vec3 voxelStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); glm::vec3 voxelStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); + glm::vec3 voxelStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); int newTop = start.y * voxelScale; int newBottom = end.y * voxelScale; char r = color.red(), g = color.green(), b = color.blue(), a = color.alpha(); @@ -2250,6 +2276,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= voxelStepY) { + bool oldCurrentSet = entryDest->isSet(); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { entryDest->setRGBA(spanner->getColorAt(pos)); @@ -2276,9 +2303,24 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons bool currentSet = entryDest->isSet(); int nextStackX = (int)stackX + 1; if (nextStackX <= innerStackWidth) { - const StackArray* nextStackDest = newStackContents.constData() + - (int)stackZ * stackWidth + (int)nextStackX; - + bool nextSetX = isSet(newStackContents, stackWidth, nextStackX, y, (int)stackZ); + if (nextSetX == currentSet) { + entryDest->clearHermiteX(); + + } else { + bool flipped = (erase == nextSetX); + float oldDistance = flipped ? 0.0f : 1.0f; + if (currentSet == oldCurrentSet && nextSetX == isSet(oldStackContents, stackWidth, + nextStackX, y, (int)stackZ)) { + oldDistance = entryDest->hermiteX[3] / (float)numeric_limits::max(); + } + if (flipped ? (spanner->intersects(pos + voxelStepX, pos, distance, normal) && + (distance = 1.0f - distance) >= oldDistance) : + (spanner->intersects(pos, pos + voxelStepX, distance, normal) && + distance <= oldDistance)) { + entryDest->setHermiteX(erase ? -normal : normal, distance); + } + } } bool nextSetY = (entryDest != stackDest->getEntryData() + stackDest->getEntryCount() - 1 && (entryDest + 1)->isSet()); @@ -2286,26 +2328,39 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest->clearHermiteY(); } else { - bool intersects; - if (erase == nextSetY) { - if ((intersects = spanner->intersects(pos + voxelStepY, pos, distance, normal))) { - distance = 1.0f - distance; - } - } else { - intersects = spanner->intersects(pos, pos + voxelStepY, distance, normal); + bool flipped = (erase == nextSetY); + float oldDistance = flipped ? 0.0f : 1.0f; + if (currentSet == oldCurrentSet && nextSetY == isSet(oldStackContents, stackWidth, + (int)stackX, y + 1, (int)stackZ)) { + oldDistance = entryDest->hermiteY[3] / (float)numeric_limits::max(); } - if (intersects) { - if (erase) { - normal = -normal; - } - entryDest->setHermiteY(normal, distance); + if (flipped ? (spanner->intersects(pos + voxelStepY, pos, distance, normal) && + (distance = 1.0f - distance) >= oldDistance) : + (spanner->intersects(pos, pos + voxelStepY, distance, normal) && + distance <= oldDistance)) { + entryDest->setHermiteY(erase ? -normal : normal, distance); } } int nextStackZ = (int)stackZ + 1; if (nextStackZ <= innerStackHeight) { - const StackArray* nextStackDest = newStackContents.constData() + - (int)nextStackZ * stackWidth + (int)stackX; - + bool nextSetZ = isSet(newStackContents, stackWidth, (int)stackX, y, nextStackZ); + if (nextSetZ == currentSet) { + entryDest->clearHermiteZ(); + + } else { + bool flipped = (erase == nextSetZ); + float oldDistance = flipped ? 0.0f : 1.0f; + if (currentSet == oldCurrentSet && nextSetZ == isSet(oldStackContents, stackWidth, + (int)stackX, y, nextStackZ)) { + oldDistance = entryDest->hermiteZ[3] / (float)numeric_limits::max(); + } + if (flipped ? (spanner->intersects(pos + voxelStepZ, pos, distance, normal) && + (distance = 1.0f - distance) >= oldDistance) : + (spanner->intersects(pos, pos + voxelStepZ, distance, normal) && + distance <= oldDistance)) { + entryDest->setHermiteZ(erase ? -normal : normal, distance); + } + } } } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 729fd72efa..9c0b6947e6 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -493,9 +493,17 @@ public: bool isZero() const; bool isMergeable(const Entry& other) const; + void setHermiteX(int x, int y, int z, int w) { hermiteX[0] = x; hermiteX[1] = y; hermiteX[2] = z; hermiteX[3] = w; } + void setHermiteX(const glm::vec3& normal, float position); + void clearHermiteX() { setHermiteX(0, 0, 0, 0); } + void setHermiteY(int x, int y, int z, int w) { hermiteY[0] = x; hermiteY[1] = y; hermiteY[2] = z; hermiteY[3] = w; } void setHermiteY(const glm::vec3& normal, float position); void clearHermiteY() { setHermiteY(0, 0, 0, 0); } + + void setHermiteZ(int x, int y, int z, int w) { hermiteZ[0] = x; hermiteZ[1] = y; hermiteZ[2] = z; hermiteZ[3] = w; } + void setHermiteZ(const glm::vec3& normal, float position); + void clearHermiteZ() { setHermiteZ(0, 0, 0, 0); } }; #pragma pack(pop) From 4153e32f57d1c8eacda05f738202d608ca99dd32 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 19 Dec 2014 18:31:49 -0800 Subject: [PATCH 18/67] Normal fix. --- interface/src/MetavoxelSystem.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d1124c7767..f91dac7d0f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2312,7 +2312,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); baseBatch.colorTextureID = _colorTextureID; baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); - Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); + Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); if (!_networkTextures.isEmpty()) { HeightfieldSplatBatch splatBatch; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index ecd1a41fd1..fc57c29edc 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2249,8 +2249,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest->material = index; } - entryDest->hermiteY[0] = glm::fract(voxelHeight) * numeric_limits::max(); + quint16 left = heightLineDest[-1]; + quint16 right = heightLineDest[1]; + quint16 up = heightLineDest[heightWidth]; + quint16 down = heightLineDest[-heightWidth]; + entryDest->setHermiteY(glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : + (left - right), 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : + (down - up))), glm::fract(voxelHeight)); } if (!stackDest->isEmpty()) { int oldBottom = stackDest->getPosition(); From 84a2a8b83d375f20e8758dd4b39e8fe7d541a5a9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Dec 2014 14:37:53 -0800 Subject: [PATCH 19/67] Working on rendering. --- interface/src/MetavoxelSystem.cpp | 75 ++++++++++++++++++++++++++++ interface/src/MetavoxelSystem.h | 3 ++ libraries/metavoxels/src/Spanner.cpp | 41 ++++++++++----- libraries/metavoxels/src/Spanner.h | 5 +- 4 files changed, 111 insertions(+), 13 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index f91dac7d0f..d4c562a870 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + // include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL #include "InterfaceConfig.h" @@ -34,6 +36,8 @@ #include "Application.h" #include "MetavoxelSystem.h" +using namespace std; + REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(CuboidRenderer) @@ -365,6 +369,12 @@ void MetavoxelSystem::render() { _baseVoxelProgram.bind(); foreach (const MetavoxelBatch& batch, _voxelBaseBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + batch.vertexBuffer->bind(); batch.indexBuffer->bind(); @@ -377,6 +387,8 @@ void MetavoxelSystem::render() { batch.vertexBuffer->release(); batch.indexBuffer->release(); + + glPopMatrix(); } _baseVoxelProgram.release(); @@ -398,6 +410,12 @@ void MetavoxelSystem::render() { _splatVoxelProgram.enableAttributeArray(_splatVoxelLocations.materialWeights); foreach (const VoxelSplatBatch& batch, _voxelSplatBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + batch.vertexBuffer->bind(); batch.indexBuffer->bind(); @@ -443,6 +461,8 @@ void MetavoxelSystem::render() { batch.vertexBuffer->release(); batch.indexBuffer->release(); + + glPopMatrix(); } glDisable(GL_POLYGON_OFFSET_FILL); @@ -474,6 +494,12 @@ void MetavoxelSystem::render() { DependencyManager::get()->bindSimpleProgram(); foreach (const HermiteBatch& batch, _hermiteBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + batch.vertexBuffer->bind(); glVertexPointer(3, GL_FLOAT, 0, 0); @@ -481,6 +507,8 @@ void MetavoxelSystem::render() { glDrawArrays(GL_LINES, 0, batch.vertexCount); batch.vertexBuffer->release(); + + glPopMatrix(); } DependencyManager::get()->releaseSimpleProgram(); @@ -1247,6 +1275,9 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation _hermite.clear(); } HermiteBatch hermiteBatch; + hermiteBatch.translation = translation; + hermiteBatch.rotation = rotation; + hermiteBatch.scale = scale; hermiteBatch.vertexBuffer = &_hermiteBuffer; hermiteBatch.vertexCount = _hermiteCount; Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); @@ -2267,6 +2298,50 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g QVector hermiteSegments; QMultiHash quadIndices; + int stackWidth = node->getStack()->getWidth(); + int stackHeight = node->getStack()->getContents().size() / stackWidth; + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; + const StackArray* src = node->getStack()->getContents().constData(); + glm::vec3 pos; + glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), + 1.0f / innerStackHeight); + bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); + + for (int z = 0; z < stackHeight; z++, pos.z += step.z) { + pos.x = 0.0f; + for (int x = 0; x < stackWidth; x++, src++, pos.x += step.x) { + if (src->isEmpty()) { + continue; + } + pos.y = src->getPosition() * step.y; + for (const StackArray::Entry* entry = src->getEntryData(), *end = entry + src->getEntryCount(); entry != end; + entry++, pos.y += step.y) { + if (displayHermite) { + glm::vec3 normal; + float distance = entry->getHermiteX(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(distance * step.x, 0.0f, 0.0f); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } + distance = entry->getHermiteY(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(0.0f, distance * step.y, 0.0f); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } + distance = entry->getHermiteZ(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(0.0f, 0.0f, distance * step.z); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } + } + } + } + } + _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, width, node->getStack()->getMaterials()); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 51efc613a4..af6480c034 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -213,6 +213,9 @@ public: class HermiteBatch { public: QOpenGLBuffer* vertexBuffer; + glm::vec3 translation; + glm::quat rotation; + glm::vec3 scale; int vertexCount; }; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index fc57c29edc..f4676bb33b 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1166,25 +1166,42 @@ bool StackArray::Entry::isMergeable(const Entry& other) const { ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); } +static inline void setHermite(uchar values[4], const glm::vec3& normal, float position) { + values[0] = normal.x * numeric_limits::max(); + values[1] = normal.y * numeric_limits::max(); + values[2] = normal.z * numeric_limits::max(); + values[3] = position * numeric_limits::max(); +} + +static inline float getHermite(const uchar values[4], glm::vec3& normal) { + normal.x = (char)values[0] / (float)numeric_limits::max(); + normal.y = (char)values[1] / (float)numeric_limits::max(); + normal.z = (char)values[2] / (float)numeric_limits::max(); + return values[3] / (float)numeric_limits::max(); +} + void StackArray::Entry::setHermiteX(const glm::vec3& normal, float position) { - hermiteX[0] = normal.x * numeric_limits::max(); - hermiteX[1] = normal.y * numeric_limits::max(); - hermiteX[2] = normal.z * numeric_limits::max(); - hermiteX[3] = position * numeric_limits::max(); + setHermite(hermiteX, normal, position); +} + +float StackArray::Entry::getHermiteX(glm::vec3& normal) const { + return getHermite(hermiteX, normal); } void StackArray::Entry::setHermiteY(const glm::vec3& normal, float position) { - hermiteY[0] = normal.x * numeric_limits::max(); - hermiteY[1] = normal.y * numeric_limits::max(); - hermiteY[2] = normal.z * numeric_limits::max(); - hermiteY[3] = position * numeric_limits::max(); + setHermite(hermiteY, normal, position); +} + +float StackArray::Entry::getHermiteY(glm::vec3& normal) const { + return getHermite(hermiteY, normal); } void StackArray::Entry::setHermiteZ(const glm::vec3& normal, float position) { - hermiteZ[0] = normal.x * numeric_limits::max(); - hermiteZ[1] = normal.y * numeric_limits::max(); - hermiteZ[2] = normal.z * numeric_limits::max(); - hermiteZ[3] = position * numeric_limits::max(); + setHermite(hermiteZ, normal, position); +} + +float StackArray::Entry::getHermiteZ(glm::vec3& normal) const { + return getHermite(hermiteZ, normal); } HeightfieldStack::HeightfieldStack(int width, const QVector& contents, diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 9c0b6947e6..d6a0f07cbd 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -496,14 +496,17 @@ public: void setHermiteX(int x, int y, int z, int w) { hermiteX[0] = x; hermiteX[1] = y; hermiteX[2] = z; hermiteX[3] = w; } void setHermiteX(const glm::vec3& normal, float position); void clearHermiteX() { setHermiteX(0, 0, 0, 0); } + float getHermiteX(glm::vec3& normal) const; void setHermiteY(int x, int y, int z, int w) { hermiteY[0] = x; hermiteY[1] = y; hermiteY[2] = z; hermiteY[3] = w; } void setHermiteY(const glm::vec3& normal, float position); void clearHermiteY() { setHermiteY(0, 0, 0, 0); } - + float getHermiteY(glm::vec3& normal) const; + void setHermiteZ(int x, int y, int z, int w) { hermiteZ[0] = x; hermiteZ[1] = y; hermiteZ[2] = z; hermiteZ[3] = w; } void setHermiteZ(const glm::vec3& normal, float position); void clearHermiteZ() { setHermiteZ(0, 0, 0, 0); } + float getHermiteZ(glm::vec3& normal) const; }; #pragma pack(pop) From 10fdb01436b6e870f93f2257833149e9fda98752 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Dec 2014 15:16:40 -0800 Subject: [PATCH 20/67] Fix for toggling Hermite display. --- interface/src/MetavoxelSystem.cpp | 8 +++++--- interface/src/MetavoxelSystem.h | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eaad7b8de0..d9be17a260 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -579,7 +579,8 @@ bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, con } void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) }; + MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, + SharedObjectPointer(), color)) }; applyEdit(edit, true); } @@ -1106,6 +1107,7 @@ VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector _vertices(vertices), _indices(indices), _hermite(hermite), + _hermiteEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)), _quadIndices(quadIndices), _size(size), _vertexCount(vertices.size()), @@ -2292,7 +2294,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // restore the default alignment; it's what Qt uses for image storage glPixelStorei(GL_UNPACK_ALIGNMENT, 4); } - if (!_voxels && node->getStack()) { + bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); + if ((!_voxels || (displayHermite && !static_cast(_voxels.data())->isHermiteEnabled())) && node->getStack()) { QVector vertices; QVector indices; QVector hermiteSegments; @@ -2306,7 +2309,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 pos; glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), 1.0f / innerStackHeight); - bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); for (int z = 0; z < stackHeight; z++, pos.z += step.z) { pos.x = 0.0f; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index af6480c034..51424be930 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -331,6 +331,8 @@ public: const QMultiHash& quadIndices, int size, const QVector& materials = QVector()); + bool isHermiteEnabled() const { return _hermiteEnabled; } + /// Finds the first intersection between the described ray and the voxel data. /// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1) bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, @@ -344,6 +346,7 @@ private: QVector _vertices; QVector _indices; QVector _hermite; + bool _hermiteEnabled; QMultiHash _quadIndices; int _size; int _vertexCount; From fbd4ca5c081a6b52bf1c8d0c3c5e230c961f4f16 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Dec 2014 18:37:06 -0800 Subject: [PATCH 21/67] More work on rendering. --- interface/src/MetavoxelSystem.cpp | 98 ++++++++++++++++------------ libraries/metavoxels/src/Spanner.cpp | 9 +++ libraries/metavoxels/src/Spanner.h | 2 + 3 files changed, 67 insertions(+), 42 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d9be17a260..84b91c80a1 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2228,24 +2228,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - QVector heightContents = node->getHeight()->getContents(); - if (node->getStack()) { - // clear any height values covered by stacks - int stackWidth = node->getStack()->getWidth(); - int stackHeight = node->getStack()->getContents().size() / stackWidth; - const QByteArray* src = node->getStack()->getContents().constData(); - quint16* dest = heightContents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; - for (int z = 0; z < stackHeight; z++, dest += width) { - quint16* lineDest = dest; - for (int x = 0; x < stackWidth; x++, src++, lineDest++) { - if (!src->isEmpty()) { - //*lineDest = 0; - } - } - } - } glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, width, height, 0, - GL_RED, GL_UNSIGNED_SHORT, heightContents.constData()); + GL_RED, GL_UNSIGNED_SHORT, node->getHeight()->getContents().constData()); glGenTextures(1, &_colorTextureID); glBindTexture(GL_TEXTURE_2D, _colorTextureID); @@ -2310,37 +2294,67 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), 1.0f / innerStackHeight); - for (int z = 0; z < stackHeight; z++, pos.z += step.z) { + for (int z = 0; z <= stackHeight; z++) { pos.x = 0.0f; - for (int x = 0; x < stackWidth; x++, src++, pos.x += step.x) { - if (src->isEmpty()) { - continue; - } - pos.y = src->getPosition() * step.y; - for (const StackArray::Entry* entry = src->getEntryData(), *end = entry + src->getEntryCount(); entry != end; - entry++, pos.y += step.y) { - if (displayHermite) { - glm::vec3 normal; - float distance = entry->getHermiteX(normal); - if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(distance * step.x, 0.0f, 0.0f); - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + const StackArray* lineSrc = src; + for (int x = 0; x <= stackWidth; x++) { + if (!lineSrc->isEmpty()) { + int y = lineSrc->getPosition(); + pos.y = y * step.y; + for (const StackArray::Entry* entry = lineSrc->getEntryData(), *end = entry + lineSrc->getEntryCount(); + entry != end; entry++, pos.y += step.y, y++) { + if (displayHermite && x != 0 && z != 0) { + glm::vec3 normal; + float distance = entry->getHermiteX(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(distance * step.x, 0.0f, 0.0f); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } + distance = entry->getHermiteY(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(0.0f, distance * step.y, 0.0f); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } + distance = entry->getHermiteZ(normal); + if (normal != glm::vec3()) { + glm::vec3 start = pos + glm::vec3(0.0f, 0.0f, distance * step.z); + hermiteSegments.append(start); + hermiteSegments.append(start + normal * step); + } } - distance = entry->getHermiteY(normal); - if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(0.0f, distance * step.y, 0.0f); - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + int alpha0 = entry->rgba[3]; + int alpha2 = lineSrc->getEntryAlpha(y + 1); + int alpha1 = alpha0, alpha4 = alpha0, alpha6 = alpha0; + int alphaTotal = alpha0 + alpha2; + int possibleTotal = 2 * numeric_limits::max(); + + // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between + // neighboring blocks, which share only one layer of points + bool middleX = (x != 0 && x != stackWidth); + bool middleZ = (z != 0 && z != stackHeight); + if (middleZ) { + alphaTotal += (alpha4 = lineSrc[stackWidth].getEntryAlpha(y)); + possibleTotal += numeric_limits::max(); + + alphaTotal += (alpha6 = lineSrc[stackWidth].getEntryAlpha(y + 1)); + possibleTotal += numeric_limits::max(); } - distance = entry->getHermiteZ(normal); - if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(0.0f, 0.0f, distance * step.z); - hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + + if (alphaTotal == 0 || alphaTotal == possibleTotal) { + continue; // no corners set/all corners set } } } + if (x != 0) { + pos.x += step.x; + lineSrc++; + } + } + if (z != 0) { + pos.z += step.z; + src += stackWidth; } } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f4676bb33b..7abb41eedc 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1204,6 +1204,15 @@ float StackArray::Entry::getHermiteZ(glm::vec3& normal) const { return getHermite(hermiteZ, normal); } +int StackArray::getEntryAlpha(int y) const { + int count = getEntryCount(); + if (count == 0) { + return 0; + } + int relative = y - getPosition(); + return (relative < count) ? getEntryData()[qMax(relative, 0)].rgba[3] : 0; +} + HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index d6a0f07cbd..5028659609 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -527,6 +527,8 @@ public: Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } + int getEntryAlpha(int y) const; + void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } }; From de4776ef08b23887e2bd00b34b87c713666ff1d1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Dec 2014 15:36:22 -0800 Subject: [PATCH 22/67] More progress on rendering. --- interface/src/MetavoxelSystem.cpp | 72 +++++++++++++++++++- libraries/metavoxels/src/Spanner.cpp | 99 +++++++++++++++++----------- libraries/metavoxels/src/Spanner.h | 26 +++----- 3 files changed, 138 insertions(+), 59 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 84b91c80a1..d6b636f243 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2294,6 +2294,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), 1.0f / innerStackHeight); + const int EDGES_PER_CUBE = 12; + EdgeCrossing crossings[EDGES_PER_CUBE]; + for (int z = 0; z <= stackHeight; z++) { pos.x = 0.0f; const StackArray* lineSrc = src; @@ -2324,9 +2327,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g hermiteSegments.append(start + normal * step); } } - int alpha0 = entry->rgba[3]; + int alpha0 = qAlpha(entry->color); int alpha2 = lineSrc->getEntryAlpha(y + 1); - int alpha1 = alpha0, alpha4 = alpha0, alpha6 = alpha0; + int alpha1 = alpha0, alpha3 = alpha2, alpha4 = alpha0, alpha6 = alpha2; int alphaTotal = alpha0 + alpha2; int possibleTotal = 2 * numeric_limits::max(); @@ -2341,10 +2344,73 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g alphaTotal += (alpha6 = lineSrc[stackWidth].getEntryAlpha(y + 1)); possibleTotal += numeric_limits::max(); } - + int alpha5 = alpha4, alpha7 = alpha6; + if (middleX) { + alphaTotal += (alpha1 = lineSrc[1].getEntryAlpha(y)); + possibleTotal += numeric_limits::max(); + + alphaTotal += (alpha3 = lineSrc[1].getEntryAlpha(y + 1)); + possibleTotal += numeric_limits::max(); + + if (middleZ) { + alphaTotal += (alpha5 = lineSrc[stackWidth + 1].getEntryAlpha(y)); + possibleTotal += numeric_limits::max(); + + alphaTotal += (alpha7 = lineSrc[stackWidth + 1].getEntryAlpha(y + 1)); + possibleTotal += numeric_limits::max(); + } + } if (alphaTotal == 0 || alphaTotal == possibleTotal) { continue; // no corners set/all corners set } + // the terrifying conditional code that follows checks each cube edge for a crossing, gathering + // its properties (color, material, normal) if one is present; as before, boundary edges are excluded + int crossingCount = 0; + if (middleX) { + if (alpha0 != alpha1) { + EdgeCrossing& crossing = crossings[crossingCount++]; + float distance = entry->getHermiteX(crossing.normal); + if (alpha0 == 0) { + const StackArray::Entry& nextEntry = lineSrc[1].getEntry(y); + crossing.color = nextEntry.color; + crossing.material = nextEntry.material; + } else { + crossing.color = entry->color; + crossing.material = entry->material; + } + crossing.point = glm::vec3(distance, 0.0f, 0.0f); + } + + } + if (alpha0 != alpha2) { + EdgeCrossing& crossing = crossings[crossingCount++]; + float distance = entry->getHermiteY(crossing.normal); + if (alpha0 == 0) { + const StackArray::Entry& nextEntry = lineSrc->getEntry(y + 1); + crossing.color = nextEntry.color; + crossing.material = nextEntry.material; + } else { + crossing.color = entry->color; + crossing.material = entry->material; + } + crossing.point = glm::vec3(0.0f, distance, 0.0f); + } + if (middleZ) { + if (alpha0 != alpha4) { + EdgeCrossing& crossing = crossings[crossingCount++]; + float distance = entry->getHermiteZ(crossing.normal); + if (alpha0 == 0) { + const StackArray::Entry& nextEntry = lineSrc[stackWidth].getEntry(y); + crossing.color = nextEntry.color; + crossing.material = nextEntry.material; + } else { + crossing.color = entry->color; + crossing.material = entry->material; + } + crossing.point = glm::vec3(0.0f, 0.0f, distance); + } + + } } } if (x != 0) { diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 7abb41eedc..737a1189e8 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1153,31 +1153,32 @@ static QVector decodeHeightfieldStack(const QByteArray& encoded, return contents; } -static inline bool isZero(const uchar values[4]) { - return *(const quint32*)values == 0; +StackArray::Entry::Entry() : + color(0), + material(0), + hermiteX(0), + hermiteY(0), + hermiteZ(0) { } bool StackArray::Entry::isZero() const { - return ::isZero(rgba) && material == 0 && ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); + return color == 0 && material == 0 && hermiteX == 0 && hermiteY == 0 && hermiteZ == 0; } bool StackArray::Entry::isMergeable(const Entry& other) const { - return *(const quint32*)rgba == *(const quint32*)other.rgba && material == other.material && - ::isZero(hermiteX) && ::isZero(hermiteY) && ::isZero(hermiteZ); + return color == other.color && material == other.material && hermiteX == 0 && hermiteY == 0 && hermiteZ == 0; } -static inline void setHermite(uchar values[4], const glm::vec3& normal, float position) { - values[0] = normal.x * numeric_limits::max(); - values[1] = normal.y * numeric_limits::max(); - values[2] = normal.z * numeric_limits::max(); - values[3] = position * numeric_limits::max(); +static inline void setHermite(quint32& value, const glm::vec3& normal, float position) { + value = qRgba(normal.x * numeric_limits::max(), normal.y * numeric_limits::max(), + normal.z * numeric_limits::max(), position * numeric_limits::max()); } -static inline float getHermite(const uchar values[4], glm::vec3& normal) { - normal.x = (char)values[0] / (float)numeric_limits::max(); - normal.y = (char)values[1] / (float)numeric_limits::max(); - normal.z = (char)values[2] / (float)numeric_limits::max(); - return values[3] / (float)numeric_limits::max(); +static inline float getHermite(QRgb value, glm::vec3& normal) { + normal.x = (char)qRed(value) / (float)numeric_limits::max(); + normal.y = (char)qGreen(value) / (float)numeric_limits::max(); + normal.z = (char)qBlue(value) / (float)numeric_limits::max(); + return qAlpha(value) / (float)numeric_limits::max(); } void StackArray::Entry::setHermiteX(const glm::vec3& normal, float position) { @@ -1210,7 +1211,27 @@ int StackArray::getEntryAlpha(int y) const { return 0; } int relative = y - getPosition(); - return (relative < count) ? getEntryData()[qMax(relative, 0)].rgba[3] : 0; + return (relative < count) ? qAlpha(getEntryData()[qMax(relative, 0)].color) : 0; +} + +StackArray::Entry& StackArray::getEntry(int y) { + static Entry emptyEntry; + int count = getEntryCount(); + if (count == 0) { + return emptyEntry; + } + int relative = y - getPosition(); + return (relative < count) ? getEntryData()[qMax(relative, 0)] : emptyEntry; +} + +const StackArray::Entry& StackArray::getEntry(int y) const { + static Entry emptyEntry; + int count = getEntryCount(); + if (count == 0) { + return emptyEntry; + } + int relative = y - getPosition(); + return (relative < count) ? getEntryData()[qMax(relative, 0)] : emptyEntry; } HeightfieldStack::HeightfieldStack(int width, const QVector& contents, @@ -2214,8 +2235,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 voxelStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); int newTop = start.y * voxelScale; int newBottom = end.y * voxelScale; - char r = color.red(), g = color.green(), b = color.blue(), a = color.alpha(); - bool erase = (a == 0); + QRgb rgba = color.rgba(); + bool erase = (color.alpha() == 0); uchar materialMaterialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); QByteArray dummyContents; uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, dummyContents); @@ -2246,8 +2267,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; int topHeight = -1; - char topR = 0, topG = 0, topB = 0; - uchar topMaterial; + QRgb topColor = 0; + uchar topMaterial = 0; if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; @@ -2259,7 +2280,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons StackArray::Entry* entryDest = stackDest->getEntryData(); if (colorDest) { - entryDest->setRGB(colorDest); + entryDest->color = qRgb(colorDest[0], colorDest[1], colorDest[2]); } if (materialDest) { @@ -2295,7 +2316,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->getEntryCount() * sizeof(StackArray::Entry)); for (StackArray::Entry* entryDest = newStack.getEntryData(), *end = entryDest + prepend; entryDest != end; entryDest++) { - entryDest->setRGBA(end->rgba); + entryDest->color = end->color; entryDest->material = end->material; } *stackDest = newStack; @@ -2311,10 +2332,10 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons bool oldCurrentSet = entryDest->isSet(); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { - entryDest->setRGBA(spanner->getColorAt(pos)); + entryDest->color = spanner->getColorAt(pos); } else { - entryDest->setRGBA(r, g, b, a); + entryDest->color = rgba; } if (hasOwnMaterials && !erase) { int index = spanner->getMaterialAt(pos); @@ -2337,14 +2358,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (nextStackX <= innerStackWidth) { bool nextSetX = isSet(newStackContents, stackWidth, nextStackX, y, (int)stackZ); if (nextSetX == currentSet) { - entryDest->clearHermiteX(); + entryDest->hermiteX = 0; } else { bool flipped = (erase == nextSetX); float oldDistance = flipped ? 0.0f : 1.0f; if (currentSet == oldCurrentSet && nextSetX == isSet(oldStackContents, stackWidth, nextStackX, y, (int)stackZ)) { - oldDistance = entryDest->hermiteX[3] / (float)numeric_limits::max(); + oldDistance = qAlpha(entryDest->hermiteX) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + voxelStepX, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : @@ -2357,14 +2378,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons bool nextSetY = (entryDest != stackDest->getEntryData() + stackDest->getEntryCount() - 1 && (entryDest + 1)->isSet()); if (nextSetY == currentSet) { - entryDest->clearHermiteY(); + entryDest->hermiteY = 0; } else { bool flipped = (erase == nextSetY); float oldDistance = flipped ? 0.0f : 1.0f; if (currentSet == oldCurrentSet && nextSetY == isSet(oldStackContents, stackWidth, (int)stackX, y + 1, (int)stackZ)) { - oldDistance = entryDest->hermiteY[3] / (float)numeric_limits::max(); + oldDistance = qAlpha(entryDest->hermiteY) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + voxelStepY, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : @@ -2377,14 +2398,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (nextStackZ <= innerStackHeight) { bool nextSetZ = isSet(newStackContents, stackWidth, (int)stackX, y, nextStackZ); if (nextSetZ == currentSet) { - entryDest->clearHermiteZ(); + entryDest->hermiteZ = 0; } else { bool flipped = (erase == nextSetZ); float oldDistance = flipped ? 0.0f : 1.0f; if (currentSet == oldCurrentSet && nextSetZ == isSet(oldStackContents, stackWidth, (int)stackX, y, nextStackZ)) { - oldDistance = entryDest->hermiteZ[3] / (float)numeric_limits::max(); + oldDistance = qAlpha(entryDest->hermiteZ) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + voxelStepZ, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : @@ -2413,10 +2434,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons StackArray::Entry* topEntry = stackDest->getEntryData() + topIndex; if (topEntry->isSet()) { topHeight = ((stackDest->getPosition() + topIndex) + - topEntry->hermiteY[3] / (float)numeric_limits::max()) / voxelScale; - topR = topEntry->rgba[0]; - topG = topEntry->rgba[1]; - topB = topEntry->rgba[2]; + qAlpha(topEntry->hermiteY) / (float)numeric_limits::max()) / voxelScale; + topColor = topEntry->color; topMaterial = topEntry->material; } else { @@ -2436,9 +2455,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (topHeight != -1) { *heightLineDest = topHeight; if (colorDest) { - colorDest[0] = topR; - colorDest[1] = topG; - colorDest[2] = topB; + colorDest[0] = qRed(topColor); + colorDest[1] = qGreen(topColor); + colorDest[2] = qBlue(topColor); } if (materialDest) { if (topMaterial != 0) { @@ -2467,9 +2486,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons colorDest[2] = qBlue(spannerColor); } else { - colorDest[0] = r; - colorDest[1] = g; - colorDest[2] = b; + colorDest[0] = color.red(); + colorDest[1] = color.green(); + colorDest[2] = color.blue(); } } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 5028659609..a12c677b83 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -477,35 +477,26 @@ public: /// A single entry within the array. class Entry { public: - uchar rgba[4]; + quint32 color; uchar material; - uchar hermiteX[4]; - uchar hermiteY[4]; - uchar hermiteZ[4]; - - void setRGBA(int r, int g, int b, int a = 0xFF) { rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a; } - void setRGBA(const uchar* orgba) { setRGBA(orgba[0], orgba[1], orgba[2], orgba[3]); } - void setRGB(const uchar* rgb) { setRGBA(rgb[0], rgb[1], rgb[2]); } - void setRGBA(QRgb rgb) { setRGBA(qRed(rgb), qGreen(rgb), qBlue(rgb), qAlpha(rgb)); } + quint32 hermiteX; + quint32 hermiteY; + quint32 hermiteZ; - bool isSet() const { return rgba[3] != 0; } + Entry(); + + bool isSet() const { return qAlpha(color) != 0; } bool isZero() const; bool isMergeable(const Entry& other) const; - void setHermiteX(int x, int y, int z, int w) { hermiteX[0] = x; hermiteX[1] = y; hermiteX[2] = z; hermiteX[3] = w; } void setHermiteX(const glm::vec3& normal, float position); - void clearHermiteX() { setHermiteX(0, 0, 0, 0); } float getHermiteX(glm::vec3& normal) const; - void setHermiteY(int x, int y, int z, int w) { hermiteY[0] = x; hermiteY[1] = y; hermiteY[2] = z; hermiteY[3] = w; } void setHermiteY(const glm::vec3& normal, float position); - void clearHermiteY() { setHermiteY(0, 0, 0, 0); } float getHermiteY(glm::vec3& normal) const; - void setHermiteZ(int x, int y, int z, int w) { hermiteZ[0] = x; hermiteZ[1] = y; hermiteZ[2] = z; hermiteZ[3] = w; } void setHermiteZ(const glm::vec3& normal, float position); - void clearHermiteZ() { setHermiteZ(0, 0, 0, 0); } float getHermiteZ(glm::vec3& normal) const; }; #pragma pack(pop) @@ -529,6 +520,9 @@ public: int getEntryAlpha(int y) const; + Entry& getEntry(int y); + const Entry& getEntry(int y) const; + void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } }; From 83967d49cf623fe4f738124e7f608758da03b351 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Dec 2014 18:16:49 -0800 Subject: [PATCH 23/67] More rendering bits. --- interface/src/MetavoxelSystem.cpp | 258 +++++++++++++++++++++++---- libraries/metavoxels/src/Spanner.cpp | 2 +- 2 files changed, 220 insertions(+), 40 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d6b636f243..8398128bf4 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1328,6 +1328,8 @@ public: glm::vec3 normal; QRgb color; char material; + + void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } }; const int MAX_NORMALS_PER_VERTEX = 4; @@ -2290,7 +2292,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; const StackArray* src = node->getStack()->getContents().constData(); - glm::vec3 pos; glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), 1.0f / innerStackHeight); @@ -2298,31 +2299,31 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g EdgeCrossing crossings[EDGES_PER_CUBE]; for (int z = 0; z <= stackHeight; z++) { - pos.x = 0.0f; const StackArray* lineSrc = src; for (int x = 0; x <= stackWidth; x++) { if (!lineSrc->isEmpty()) { int y = lineSrc->getPosition(); - pos.y = y * step.y; + NormalIndex lastIndexY; for (const StackArray::Entry* entry = lineSrc->getEntryData(), *end = entry + lineSrc->getEntryCount(); - entry != end; entry++, pos.y += step.y, y++) { + entry != end; entry++, y++) { + int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (displayHermite && x != 0 && z != 0) { glm::vec3 normal; float distance = entry->getHermiteX(normal); if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(distance * step.x, 0.0f, 0.0f); + glm::vec3 start = glm::vec3(clampedX + distance, y, clampedZ) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } distance = entry->getHermiteY(normal); if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(0.0f, distance * step.y, 0.0f); + glm::vec3 start = glm::vec3(clampedX, y + distance, clampedZ) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } distance = entry->getHermiteZ(normal); if (normal != glm::vec3()) { - glm::vec3 start = pos + glm::vec3(0.0f, 0.0f, distance * step.z); + glm::vec3 start = glm::vec3(clampedX, y, clampedZ + distance) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } @@ -2366,60 +2367,239 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded int crossingCount = 0; + const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1); if (middleX) { + const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y); + const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1); if (alpha0 != alpha1) { EdgeCrossing& crossing = crossings[crossingCount++]; - float distance = entry->getHermiteX(crossing.normal); - if (alpha0 == 0) { - const StackArray::Entry& nextEntry = lineSrc[1].getEntry(y); - crossing.color = nextEntry.color; - crossing.material = nextEntry.material; - } else { - crossing.color = entry->color; - crossing.material = entry->material; - } - crossing.point = glm::vec3(distance, 0.0f, 0.0f); + crossing.point = glm::vec3(entry->getHermiteX(crossing.normal), 0.0f, 0.0f); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryX : *entry); + } + if (alpha1 != alpha3) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(1.0f, nextEntryX.getHermiteY(crossing.normal), 0.0f); + crossing.setColorMaterial(alpha1 == 0 ? nextEntryXY : nextEntryX); + } + if (alpha2 != alpha3) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(nextEntryY.getHermiteX(crossing.normal), 1.0f, 0.0f); + crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); + } + if (middleZ) { + const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y); + const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(y); + const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry(y + 1); + if (alpha1 != alpha5) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); + crossing.setColorMaterial(alpha1 == 0 ? nextEntryXZ : nextEntryX); + } + if (alpha3 != alpha7) { + EdgeCrossing& crossing = crossings[crossingCount++]; + const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1); + crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); + crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); + } + if (alpha4 != alpha5) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(nextEntryZ.getHermiteX(crossing.normal), 0.0f, 1.0f); + crossing.setColorMaterial(alpha4 == 0 ? nextEntryXZ : nextEntryZ); + } + if (alpha5 != alpha7) { + EdgeCrossing& crossing = crossings[crossingCount++]; + const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(y); + crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); + crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); + } + if (alpha6 != alpha7) { + EdgeCrossing& crossing = crossings[crossingCount++]; + const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1); + crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); + crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); + } } - } if (alpha0 != alpha2) { EdgeCrossing& crossing = crossings[crossingCount++]; - float distance = entry->getHermiteY(crossing.normal); - if (alpha0 == 0) { - const StackArray::Entry& nextEntry = lineSrc->getEntry(y + 1); - crossing.color = nextEntry.color; - crossing.material = nextEntry.material; - } else { - crossing.color = entry->color; - crossing.material = entry->material; - } - crossing.point = glm::vec3(0.0f, distance, 0.0f); + crossing.point = glm::vec3(0.0f, entry->getHermiteY(crossing.normal), 0.0f); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : *entry); } if (middleZ) { + const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y); + const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1); if (alpha0 != alpha4) { EdgeCrossing& crossing = crossings[crossingCount++]; - float distance = entry->getHermiteZ(crossing.normal); - if (alpha0 == 0) { - const StackArray::Entry& nextEntry = lineSrc[stackWidth].getEntry(y); - crossing.color = nextEntry.color; - crossing.material = nextEntry.material; - } else { - crossing.color = entry->color; - crossing.material = entry->material; + crossing.point = glm::vec3(0.0f, 0.0f, entry->getHermiteZ(crossing.normal)); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryZ : *entry); + } + if (alpha2 != alpha6) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(0.0f, 1.0f, nextEntryY.getHermiteZ(crossing.normal)); + crossing.setColorMaterial(alpha2 == 0 ? nextEntryYZ : nextEntryY); + } + if (alpha4 != alpha6) { + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.point = glm::vec3(0.0f, nextEntryZ.getHermiteY(crossing.normal), 1.0f); + crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ); + } + } + glm::vec3 center; + glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; + int normalCount = 0; + const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); + const int MAX_MATERIALS_PER_VERTEX = 4; + quint8 materials[] = { 0, 0, 0, 0 }; + glm::vec4 materialWeights; + float totalWeight = 0.0f; + int red = 0, green = 0, blue = 0; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + center += crossing.point; + + int j = 0; + for (; j < normalCount; j++) { + if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { + normals[j] = safeNormalize(normals[j] + crossing.normal); + break; } - crossing.point = glm::vec3(0.0f, 0.0f, distance); + } + if (j == normalCount) { + normals[normalCount++] = crossing.normal; } + red += qRed(crossing.color); + green += qGreen(crossing.color); + blue += qBlue(crossing.color); + + // when assigning a material, search for its presence and, if not found, + // place it in the first empty slot + if (crossing.material != 0) { + for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { + if (materials[j] == crossing.material) { + materialWeights[j] += 1.0f; + totalWeight += 1.0f; + break; + + } else if (materials[j] == 0) { + materials[j] = crossing.material; + materialWeights[j] = 1.0f; + totalWeight += 1.0f; + break; + } + } + } } + center /= crossingCount; + + // use a sequence of Givens rotations to perform a QR decomposition + // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf + glm::mat4 r(0.0f); + glm::vec4 bottom; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); + + for (int j = 0; j < 4; j++) { + float angle = glm::atan(-bottom[j], r[j][j]); + float sina = glm::sin(angle); + float cosa = glm::cos(angle); + + for (int k = 0; k < 4; k++) { + float tmp = bottom[k]; + bottom[k] = sina * r[k][j] + cosa * tmp; + r[k][j] = cosa * r[k][j] - sina * tmp; + } + } + } + + // extract the submatrices, form ata + glm::mat3 a(r); + glm::vec3 b(r[3]); + glm::mat3 atrans = glm::transpose(a); + glm::mat3 ata = atrans * a; + + // find the eigenvalues and eigenvectors of ata + // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) + glm::mat3 d = ata; + glm::quat combinedRotation; + const int MAX_ITERATIONS = 20; + for (int i = 0; i < MAX_ITERATIONS; i++) { + glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); + int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? + (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); + const float DESIRED_PRECISION = 0.00001f; + if (offDiagonals[largestIndex] < DESIRED_PRECISION) { + break; + } + int largestJ = (largestIndex == 2) ? 1 : 0; + int largestI = (largestIndex == 0) ? 1 : 2; + float sjj = d[largestJ][largestJ]; + float sii = d[largestI][largestI]; + float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; + glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : + (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); + combinedRotation = glm::normalize(rotation * combinedRotation); + glm::mat3 matrix = glm::mat3_cast(combinedRotation); + d = matrix * ata * glm::transpose(matrix); + } + + // form the singular matrix from the eigenvalues + const float MIN_SINGULAR_THRESHOLD = 0.1f; + d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; + d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; + d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; + + // compute the pseudo-inverse, ataplus, and use to find the minimizing solution + glm::mat3 u = glm::mat3_cast(combinedRotation); + glm::mat3 ataplus = glm::transpose(u) * d * u; + glm::vec3 solution = (ataplus * atrans * b) + center; + + // make sure it doesn't fall beyond the cell boundaries + center = glm::clamp(solution, 0.0f, 1.0f); + + if (totalWeight > 0.0f) { + materialWeights *= (numeric_limits::max() / totalWeight); + } + VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * step, + { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, + { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, + { materials[0], materials[1], materials[2], materials[3] }, + { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], + (quint8)materialWeights[3] } }; + + NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; + vertices.append(point); + for (int i = 1; i < normalCount; i++) { + index.indices[i] = vertices.size(); + point.setNormal(normals[i]); + vertices.append(point); + } + + // the first x, y, and z are repeated for the boundary edge; past that, we consider generating + // quads for each edge that includes a transition, using indices of previously generated vertices + if (x != 0 && z != 0) { + if (alpha0 != alpha1) { + + } + + if (alpha0 != alpha2) { + + } + + if (alpha0 != alpha4) { + + } + } + + lastIndexY = index; } } if (x != 0) { - pos.x += step.x; lineSrc++; } } if (z != 0) { - pos.z += step.z; src += stackWidth; } } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 737a1189e8..c27df63acf 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1178,7 +1178,7 @@ static inline float getHermite(QRgb value, glm::vec3& normal) { normal.x = (char)qRed(value) / (float)numeric_limits::max(); normal.y = (char)qGreen(value) / (float)numeric_limits::max(); normal.z = (char)qBlue(value) / (float)numeric_limits::max(); - return qAlpha(value) / (float)numeric_limits::max(); + return qAlpha(value) / (float)numeric_limits::max(); } void StackArray::Entry::setHermiteX(const glm::vec3& normal, float position) { From 08dfd28ec0a334e8442afd5e22e8bfaf5b13bf92 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 31 Dec 2014 15:53:38 -0800 Subject: [PATCH 24/67] More rendering bits. --- interface/src/MetavoxelSystem.cpp | 131 ++++++++++++++++++++++++++- libraries/metavoxels/src/Spanner.cpp | 82 ----------------- 2 files changed, 129 insertions(+), 84 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 8398128bf4..b477ed41d8 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1338,9 +1338,20 @@ class NormalIndex { public: int indices[MAX_NORMALS_PER_VERTEX]; + bool isValid() const; + int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; }; +bool NormalIndex::isValid() const { + for (int i = 0; i < MAX_NORMALS_PER_VERTEX; i++) { + if (indices[i] != 0) { + return true; + } + } + return false; +} + int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { int firstIndex = indices[0]; int closestIndex = firstIndex; @@ -2157,6 +2168,22 @@ HeightfieldNodeRenderer::~HeightfieldNodeRenderer() { Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); } +class IndexVector : public QVector { +public: + + int position; + + void swap(IndexVector& other) { QVector::swap(other); qSwap(position, other.position); } + + const NormalIndex& get(int y) const; +}; + +const NormalIndex& IndexVector::get(int y) const { + static NormalIndex nullIndex = { { 0, 0, 0, 0 } }; + int relative = y - position; + return (relative >= 0 && relative < size()) ? at(relative) : nullIndex; +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -2298,12 +2325,23 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; + IndexVector indicesX; + IndexVector lastIndicesX; + QVector indicesZ(stackWidth + 1); + QVector lastIndicesZ(stackWidth + 1); + for (int z = 0; z <= stackHeight; z++) { const StackArray* lineSrc = src; for (int x = 0; x <= stackWidth; x++) { if (!lineSrc->isEmpty()) { - int y = lineSrc->getPosition(); + int position = lineSrc->getPosition(); + int count = lineSrc->getEntryCount(); + int y = position; NormalIndex lastIndexY; + indicesX.position = position; + indicesX.resize(count); + indicesZ[x].position = position; + indicesZ[x].resize(count); for (const StackArray::Entry* entry = lineSrc->getEntryData(), *end = entry + lineSrc->getEntryCount(); entry != end; entry++, y++) { int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); @@ -2579,29 +2617,118 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the first x, y, and z are repeated for the boundary edge; past that, we consider generating // quads for each edge that includes a transition, using indices of previously generated vertices if (x != 0 && z != 0) { + int reclampedX = qMin(clampedX, stackWidth - 1); + int reclampedZ = qMin(clampedZ, stackHeight - 1); if (alpha0 != alpha1) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (y > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + } + if (reclampedZ > 0) { + if (y > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); + } + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); + } + const NormalIndex& index1 = lastIndexY; + const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); + const NormalIndex& index3 = lastIndicesZ[x].get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative x + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive x + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha2) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); + } + } + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); + } + const NormalIndex& index1 = lastIndicesZ[x].get(y); + const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); + const NormalIndex& index3 = lastIndicesX.get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, + vertices.at(index1.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative y + indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } else { // quad faces positive y + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha4) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); + if (y > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); + } + } + if (y > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + } + const NormalIndex& index1 = lastIndexY; + const NormalIndex& index2 = lastIndicesX.get(y - 1); + const NormalIndex& index3 = lastIndicesX.get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative z + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive z + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } } - lastIndexY = index; + indicesX[y - position] = index; + indicesZ[x][y - position] = index; } } if (x != 0) { lineSrc++; } + indicesX.swap(lastIndicesX); } if (z != 0) { src += stackWidth; } + indicesZ.swap(lastIndicesZ); } _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, width, node->getStack()->getMaterials()); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index c27df63acf..e47182326c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2226,7 +2226,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, start.y, startZ, 1.0f)); glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); - glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, start.y - end.y, 0.0f, 0.0f)); glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); float voxelStep = scale.x / innerHeightWidth; float voxelScale = scale.y / (numeric_limits::max() * voxelStep); @@ -2237,7 +2236,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int newBottom = end.y * voxelScale; QRgb rgba = color.rgba(); bool erase = (color.alpha() == 0); - uchar materialMaterialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); QByteArray dummyContents; uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, dummyContents); bool hasOwnColors = spanner->hasOwnColors(); @@ -2248,7 +2246,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 worldPos = worldStart; for (float x = startX; x >= endX; x -= stepX, worldPos -= worldStepX) { quint16* heightLineDest = heightDest + (int)x; - glm::vec3 endPos = worldPos - worldStepY; float distance; glm::vec3 normal; @@ -2266,10 +2263,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; - int topHeight = -1; - QRgb topColor = 0; - uchar topMaterial = 0; - if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; if (stackDest->isEmpty() && *heightLineDest != 0) { @@ -2423,24 +2416,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons endPruneCount++; } if (endPruneCount == stackDest->getEntryCount()) { - topHeight = 0; stackDest->clear(); } else { - int topIndex = stackDest->getEntryCount() - 1; - while (topIndex > 0 && !stackDest->getEntryData()[topIndex].isSet()) { - topIndex--; - } - StackArray::Entry* topEntry = stackDest->getEntryData() + topIndex; - if (topEntry->isSet()) { - topHeight = ((stackDest->getPosition() + topIndex) + - qAlpha(topEntry->hermiteY) / (float)numeric_limits::max()) / voxelScale; - topColor = topEntry->color; - topMaterial = topEntry->material; - - } else { - topHeight = 0; - } stackDest->removeEntries(stackDest->getEntryCount() - endPruneCount, endPruneCount); int beginningPruneCount = 0; for (int i = 0; i < stackDest->getEntryCount() - 1 && stackDest->getEntryData()[i].isMergeable( @@ -2451,66 +2429,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->getPositionRef() += beginningPruneCount; } } - - if (topHeight != -1) { - *heightLineDest = topHeight; - if (colorDest) { - colorDest[0] = qRed(topColor); - colorDest[1] = qGreen(topColor); - colorDest[2] = qBlue(topColor); - } - if (materialDest) { - if (topMaterial != 0) { - topMaterial = getMaterialIndex(newStackMaterials.at(topMaterial - 1), - newMaterialMaterials, newMaterialContents); - } - *materialDest = topMaterial; - } - } else if (erase) { - if (spanner->intersects(endPos, worldPos, distance, normal)) { - quint16 height = glm::round(glm::mix(end.y, start.y, distance)); - if (height <= *heightLineDest) { - *heightLineDest = height; - } - } - } else if (spanner->intersects(worldPos, endPos, distance, normal)) { - quint16 height = glm::round(glm::mix(start.y, end.y, distance)); - if (height >= *heightLineDest) { - *heightLineDest = height; - - if (colorDest) { - if (hasOwnColors) { - QRgb spannerColor = spanner->getColorAt(glm::mix(endPos, worldPos, distance)); - colorDest[0] = qRed(spannerColor); - colorDest[1] = qGreen(spannerColor); - colorDest[2] = qBlue(spannerColor); - - } else { - colorDest[0] = color.red(); - colorDest[1] = color.green(); - colorDest[2] = color.blue(); - } - } - - if (materialDest) { - if (hasOwnMaterials) { - int index = spanner->getMaterialAt(glm::mix(endPos, worldPos, distance)); - if (index != 0) { - int& mapping = materialMappings[index]; - if (mapping == 0) { - mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), - newMaterialMaterials, newMaterialContents); - } - index = mapping; - } - *materialDest = index; - - } else { - *materialDest = materialMaterialIndex; - } - } - } - } } } clearUnusedMaterials(newMaterialMaterials, newMaterialContents); From e242459ea2d6ec9b6d5e911cce9d7d09b918f06f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 31 Dec 2014 17:33:43 -0800 Subject: [PATCH 25/67] More render progress. --- interface/src/MetavoxelSystem.cpp | 69 +++++++++++++++++----------- libraries/metavoxels/src/Spanner.cpp | 11 ++++- libraries/metavoxels/src/Spanner.h | 2 + 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b477ed41d8..7e5714ef0f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2331,42 +2331,57 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g QVector lastIndicesZ(stackWidth + 1); for (int z = 0; z <= stackHeight; z++) { + bool middleZ = (z != 0 && z != stackHeight); const StackArray* lineSrc = src; for (int x = 0; x <= stackWidth; x++) { - if (!lineSrc->isEmpty()) { - int position = lineSrc->getPosition(); - int count = lineSrc->getEntryCount(); - int y = position; + bool middleX = (x != 0 && x != stackWidth); + + // find the y extents of this and the neighboring columns + int minimumY = INT_MAX, maximumY = -1; + lineSrc->getExtents(minimumY, maximumY); + if (middleX) { + lineSrc[1].getExtents(minimumY, maximumY); + if (middleZ) { + lineSrc[stackWidth + 1].getExtents(minimumY, maximumY); + } + } + if (middleZ) { + lineSrc[stackWidth].getExtents(minimumY, maximumY); + } + + if (maximumY >= minimumY) { + int position = minimumY; + int count = maximumY - minimumY + 1; NormalIndex lastIndexY; indicesX.position = position; indicesX.resize(count); indicesZ[x].position = position; indicesZ[x].resize(count); - for (const StackArray::Entry* entry = lineSrc->getEntryData(), *end = entry + lineSrc->getEntryCount(); - entry != end; entry++, y++) { + for (int y = position, end = position + count; y < end; y++) { + const StackArray::Entry& entry = lineSrc->getEntry(y); int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (displayHermite && x != 0 && z != 0) { glm::vec3 normal; - float distance = entry->getHermiteX(normal); + float distance = entry.getHermiteX(normal); if (normal != glm::vec3()) { glm::vec3 start = glm::vec3(clampedX + distance, y, clampedZ) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } - distance = entry->getHermiteY(normal); + distance = entry.getHermiteY(normal); if (normal != glm::vec3()) { glm::vec3 start = glm::vec3(clampedX, y + distance, clampedZ) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } - distance = entry->getHermiteZ(normal); + distance = entry.getHermiteZ(normal); if (normal != glm::vec3()) { glm::vec3 start = glm::vec3(clampedX, y, clampedZ + distance) * step; hermiteSegments.append(start); hermiteSegments.append(start + normal * step); } } - int alpha0 = qAlpha(entry->color); + int alpha0 = qAlpha(entry.color); int alpha2 = lineSrc->getEntryAlpha(y + 1); int alpha1 = alpha0, alpha3 = alpha2, alpha4 = alpha0, alpha6 = alpha2; int alphaTotal = alpha0 + alpha2; @@ -2374,8 +2389,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between // neighboring blocks, which share only one layer of points - bool middleX = (x != 0 && x != stackWidth); - bool middleZ = (z != 0 && z != stackHeight); if (middleZ) { alphaTotal += (alpha4 = lineSrc[stackWidth].getEntryAlpha(y)); possibleTotal += numeric_limits::max(); @@ -2411,8 +2424,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1); if (alpha0 != alpha1) { EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(entry->getHermiteX(crossing.normal), 0.0f, 0.0f); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryX : *entry); + crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryX : entry); } if (alpha1 != alpha3) { EdgeCrossing& crossing = crossings[crossingCount++]; @@ -2460,16 +2473,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha0 != alpha2) { EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, entry->getHermiteY(crossing.normal), 0.0f); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : *entry); + crossing.point = glm::vec3(0.0f, entry.getHermiteY(crossing.normal), 0.0f); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); } if (middleZ) { const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y); const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1); if (alpha0 != alpha4) { EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.point = glm::vec3(0.0f, 0.0f, entry->getHermiteZ(crossing.normal)); - crossing.setColorMaterial(alpha0 == 0 ? nextEntryZ : *entry); + crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); + crossing.setColorMaterial(alpha0 == 0 ? nextEntryZ : entry); } if (alpha2 != alpha6) { EdgeCrossing& crossing = crossings[crossingCount++]; @@ -2672,13 +2685,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g vertices.at(index1.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative y - indices.append(index1.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } else { // quad faces positive y - indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive y + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } @@ -2704,13 +2717,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative z - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive z - indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); + } else { // quad faces positive z + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index e47182326c..8b1983b90b 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1234,6 +1234,15 @@ const StackArray::Entry& StackArray::getEntry(int y) const { return (relative < count) ? getEntryData()[qMax(relative, 0)] : emptyEntry; } +void StackArray::getExtents(int& minimumY, int& maximumY) const { + int count = getEntryCount(); + if (count > 0) { + int position = getPosition(); + minimumY = qMin(minimumY, position); + maximumY = qMax(maximumY, position + count - 1); + } +} + HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), @@ -2265,7 +2274,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; - if (stackDest->isEmpty() && *heightLineDest != 0) { + if (stackDest->isEmpty() && *heightLineDest != 0 && false) { // initialize from heightfield *stackDest = StackArray(1); float voxelHeight = *heightLineDest * voxelScale; diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index a12c677b83..fa51a2f0d8 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -523,6 +523,8 @@ public: Entry& getEntry(int y); const Entry& getEntry(int y) const; + void getExtents(int& minimumY, int& maximumY) const; + void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } }; From 3959f99910c2f0c819d9b576668eaa0a6aa42d3c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 Jan 2015 13:57:27 -0800 Subject: [PATCH 26/67] Fixed voxel textures. --- .../shaders/metavoxel_voxel_splat.vert | 5 ++- interface/src/MetavoxelSystem.cpp | 33 ++++++++++--------- interface/src/MetavoxelSystem.h | 1 + libraries/metavoxels/src/Spanner.cpp | 4 +++ 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/interface/resources/shaders/metavoxel_voxel_splat.vert b/interface/resources/shaders/metavoxel_voxel_splat.vert index 31ddbef395..1cd1bfb6ba 100644 --- a/interface/resources/shaders/metavoxel_voxel_splat.vert +++ b/interface/resources/shaders/metavoxel_voxel_splat.vert @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the splat texture offset +uniform vec3 splatTextureOffset; + // the splat textures scales on the S axis uniform vec4 splatTextureScalesS; @@ -43,7 +46,7 @@ void main(void) { normal = gl_Normal; // pass along the scaled/offset texture coordinates - vec4 textureSpacePosition = gl_Vertex.xyyz; + vec4 textureSpacePosition = (gl_Vertex.xyz + splatTextureOffset).xyyz; gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], splatTextureScalesS[0], splatTextureScalesT[0]); gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7e5714ef0f..10dbdcb17d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -428,8 +428,9 @@ void MetavoxelSystem::render() { GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); _splatVoxelProgram.setAttributeBuffer(_splatVoxelLocations.materialWeights, GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); - + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureOffset, batch.splatTextureOffset); _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesS, batch.splatTextureScalesS); _splatVoxelProgram.setUniform(_splatVoxelLocations.splatTextureScalesT, batch.splatTextureScalesT); _splatVoxelProgram.setUniformValue( @@ -1244,7 +1245,11 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation splatBatch.indexBuffer = &_indexBuffer; splatBatch.vertexCount = _vertexCount; splatBatch.indexCount = _indexCount; - + splatBatch.splatTextureOffset = glm::vec3( + glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, + glm::dot(translation, rotation * glm::vec3(0.0f, 1.0f, 0.0f)) / scale.y, + glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); + for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { for (int j = 0; j < SPLAT_COUNT; j++) { int index = i + j; @@ -1252,8 +1257,8 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast(_materials.at(index).data()); - splatBatch.splatTextureScalesS[j] = 1.0f / material->getScaleS(); - splatBatch.splatTextureScalesT[j] = 1.0f / material->getScaleT(); + splatBatch.splatTextureScalesS[j] = scale.x / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = scale.z / material->getScaleT(); splatBatch.splatTextureIDs[j] = texture->getID(); } else { @@ -2317,10 +2322,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g int stackWidth = node->getStack()->getWidth(); int stackHeight = node->getStack()->getContents().size() / stackWidth; int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; - int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; const StackArray* src = node->getStack()->getContents().constData(); - glm::vec3 step(1.0f / innerStackWidth, scale.x / (innerStackWidth * scale.y), - 1.0f / innerStackHeight); const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; @@ -2329,6 +2331,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g IndexVector lastIndicesX; QVector indicesZ(stackWidth + 1); QVector lastIndicesZ(stackWidth + 1); + float scale = 1.0f / innerStackWidth; for (int z = 0; z <= stackHeight; z++) { bool middleZ = (z != 0 && z != stackHeight); @@ -2364,21 +2367,21 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal; float distance = entry.getHermiteX(normal); if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX + distance, y, clampedZ) * step; + glm::vec3 start = glm::vec3(clampedX + distance, y, clampedZ) * scale; hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + hermiteSegments.append(start + normal * scale); } distance = entry.getHermiteY(normal); if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX, y + distance, clampedZ) * step; + glm::vec3 start = glm::vec3(clampedX, y + distance, clampedZ) * scale; hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + hermiteSegments.append(start + normal * scale); } distance = entry.getHermiteZ(normal); if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX, y, clampedZ + distance) * step; + glm::vec3 start = glm::vec3(clampedX, y, clampedZ + distance) * scale; hermiteSegments.append(start); - hermiteSegments.append(start + normal * step); + hermiteSegments.append(start + normal * scale); } } int alpha0 = qAlpha(entry.color); @@ -2612,7 +2615,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (totalWeight > 0.0f) { materialWeights *= (numeric_limits::max() / totalWeight); } - VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * step, + VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, @@ -2748,7 +2751,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (_voxels) { - _voxels->render(translation, rotation, scale, cursor); + _voxels->render(translation, rotation, glm::vec3(scale.x, scale.x, scale.x), cursor); } if (cursor) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 51424be930..1a43c36464 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -203,6 +203,7 @@ public: /// A batch containing a voxel splat. class VoxelSplatBatch : public MetavoxelBatch { public: + glm::vec3 splatTextureOffset; int splatTextureIDs[4]; glm::vec4 splatTextureScalesS; glm::vec4 splatTextureScalesT; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 8b1983b90b..69e461f916 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1178,6 +1178,10 @@ static inline float getHermite(QRgb value, glm::vec3& normal) { normal.x = (char)qRed(value) / (float)numeric_limits::max(); normal.y = (char)qGreen(value) / (float)numeric_limits::max(); normal.z = (char)qBlue(value) / (float)numeric_limits::max(); + float length = glm::length(normal); + if (length > 0.0f) { + normal /= length; + } return qAlpha(value) / (float)numeric_limits::max(); } From a504d03b14eae26488ea84d1d448275b3dc36088 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 Jan 2015 17:14:17 -0800 Subject: [PATCH 27/67] Normal fix. --- interface/src/MetavoxelSystem.cpp | 8 +++--- libraries/metavoxels/src/Spanner.cpp | 37 ++++++++++++---------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 10dbdcb17d..2d7509f46f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2688,11 +2688,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g vertices.at(index1.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative y - indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive y - indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } @@ -2720,11 +2720,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative z - indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } else { // quad faces positive z - indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 69e461f916..77464d6226 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2119,8 +2119,8 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: } static inline bool isSet(const StackArray& array, int y) { - return !array.isEmpty() && y >= array.getPosition() && y < array.getPosition() + array.getEntryCount() && - array.getEntryData()[y - array.getPosition()].isSet(); + return !array.isEmpty() && y < array.getPosition() + array.getEntryCount() && + array.getEntryData()[qMax(0, y - array.getPosition())].isSet(); } static inline bool isSet(const QVector& stackContents, int stackWidth, int x, int y, int z) { @@ -2232,21 +2232,16 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 start = glm::ceil(transformedBounds.maximum); glm::vec3 end = glm::floor(transformedBounds.minimum); - float stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); - float stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); - float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); - glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, start.y, startZ, 1.0f)); - glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); - glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); float voxelStep = scale.x / innerHeightWidth; float voxelScale = scale.y / (numeric_limits::max() * voxelStep); - glm::vec3 voxelStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); - glm::vec3 voxelStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); - glm::vec3 voxelStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); int newTop = start.y * voxelScale; int newBottom = end.y * voxelScale; + glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, newTop / voxelScale, startZ, 1.0f)); + glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); + glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); QRgb rgba = color.rgba(); bool erase = (color.alpha() == 0); QByteArray dummyContents; @@ -2254,10 +2249,10 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); QHash materialMappings; - for (float z = startZ; z >= endZ; z -= stepZ, worldStart -= worldStepZ) { + for (float z = startZ; z >= endZ; z -= 1.0f, worldStart -= worldStepZ) { quint16* heightDest = newHeightContents.data() + (int)z * heightWidth; glm::vec3 worldPos = worldStart; - for (float x = startX; x >= endX; x -= stepX, worldPos -= worldStepX) { + for (float x = startX; x >= endX; x -= 1.0f, worldPos -= worldStepX) { quint16* heightLineDest = heightDest + (int)x; float distance; glm::vec3 normal; @@ -2333,8 +2328,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->setPosition(newBottom); } StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); - glm::vec3 pos = glm::vec3(transform * glm::vec4(x, 0.0f, z, 1.0f)) + voxelStepY * (float)newTop; - for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= voxelStepY) { + glm::vec3 pos = worldPos; + for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { bool oldCurrentSet = entryDest->isSet(); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { @@ -2373,9 +2368,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons nextStackX, y, (int)stackZ)) { oldDistance = qAlpha(entryDest->hermiteX) / (float)numeric_limits::max(); } - if (flipped ? (spanner->intersects(pos + voxelStepX, pos, distance, normal) && + if (flipped ? (spanner->intersects(pos + worldStepX, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + voxelStepX, distance, normal) && + (spanner->intersects(pos, pos + worldStepX, distance, normal) && distance <= oldDistance)) { entryDest->setHermiteX(erase ? -normal : normal, distance); } @@ -2393,9 +2388,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons (int)stackX, y + 1, (int)stackZ)) { oldDistance = qAlpha(entryDest->hermiteY) / (float)numeric_limits::max(); } - if (flipped ? (spanner->intersects(pos + voxelStepY, pos, distance, normal) && + if (flipped ? (spanner->intersects(pos + worldStepY, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + voxelStepY, distance, normal) && + (spanner->intersects(pos, pos + worldStepY, distance, normal) && distance <= oldDistance)) { entryDest->setHermiteY(erase ? -normal : normal, distance); } @@ -2413,9 +2408,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons (int)stackX, y, nextStackZ)) { oldDistance = qAlpha(entryDest->hermiteZ) / (float)numeric_limits::max(); } - if (flipped ? (spanner->intersects(pos + voxelStepZ, pos, distance, normal) && + if (flipped ? (spanner->intersects(pos + worldStepZ, pos, distance, normal) && (distance = 1.0f - distance) >= oldDistance) : - (spanner->intersects(pos, pos + voxelStepZ, distance, normal) && + (spanner->intersects(pos, pos + worldStepZ, distance, normal) && distance <= oldDistance)) { entryDest->setHermiteZ(erase ? -normal : normal, distance); } From 547c6db0a4ffc2c40311eb6cdbdd5d5b3f772946 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 2 Jan 2015 17:43:41 -0800 Subject: [PATCH 28/67] Slight simplification. --- interface/src/MetavoxelSystem.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2d7509f46f..4dddc20b4b 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2365,21 +2365,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (displayHermite && x != 0 && z != 0) { glm::vec3 normal; - float distance = entry.getHermiteX(normal); - if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX + distance, y, clampedZ) * scale; + if (entry.hermiteX != 0) { + glm::vec3 start = glm::vec3(clampedX + entry.getHermiteX(normal), y, clampedZ) * scale; hermiteSegments.append(start); hermiteSegments.append(start + normal * scale); } - distance = entry.getHermiteY(normal); - if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX, y + distance, clampedZ) * scale; + if (entry.hermiteY != 0) { + glm::vec3 start = glm::vec3(clampedX, y + entry.getHermiteY(normal), clampedZ) * scale; hermiteSegments.append(start); hermiteSegments.append(start + normal * scale); } - distance = entry.getHermiteZ(normal); - if (normal != glm::vec3()) { - glm::vec3 start = glm::vec3(clampedX, y, clampedZ + distance) * scale; + if (entry.hermiteZ != 0) { + glm::vec3 start = glm::vec3(clampedX, y, clampedZ + entry.getHermiteZ(normal)) * scale; hermiteSegments.append(start); hermiteSegments.append(start + normal * scale); } From 808cb2e4bcf1311f04586c5b9324c46cf3e49f7f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 Jan 2015 14:33:40 -0800 Subject: [PATCH 29/67] Substantial cleanup, working on interface between heightfields and dual contour surfaces. --- interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- interface/src/MetavoxelSystem.cpp | 1017 +++-------------- interface/src/MetavoxelSystem.h | 26 - .../metavoxels/src/AttributeRegistry.cpp | 856 +------------- libraries/metavoxels/src/AttributeRegistry.h | 237 ---- .../metavoxels/src/MetavoxelMessages.cpp | 292 ----- libraries/metavoxels/src/Spanner.cpp | 150 ++- libraries/metavoxels/src/Spanner.h | 67 ++ 9 files changed, 325 insertions(+), 2324 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2147dd9220..4d2ec242bd 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -465,7 +465,7 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true); + addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, SLOT(showMetavoxelNetworkSimulator())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 27e57983af..19e1808467 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -437,9 +437,9 @@ namespace MenuOption { const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces"; const QString RenderFocusIndicator = "Show Eye Focus"; const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; + const QString RenderHeightfields = "Render Heightfields"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; - const QString RenderSpanners = "Render Spanners"; const QString RenderTargetFramerate = "Framerate"; const QString RenderTargetFramerateUnlimited = "Unlimited"; const QString RenderTargetFramerate60 = "60"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4dddc20b4b..d1336baf9e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -63,11 +63,6 @@ MetavoxelSystem::~MetavoxelSystem() { void MetavoxelSystem::init() { MetavoxelClientManager::init(); - _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( - new BufferDataAttribute("voxelBuffer")); - _voxelBufferAttribute->setLODThresholdMultiplier( - AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/metavoxel_heightfield_base.vert"); _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + @@ -206,7 +201,7 @@ void MetavoxelSystem::render() { RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); - if (!_heightfieldBaseBatches.isEmpty()) { + if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -340,19 +335,17 @@ void MetavoxelSystem::render() { glDisable(GL_POLYGON_OFFSET_FILL); glDepthMask(true); glDepthFunc(GL_LESS); - - _heightfieldSplatBatches.clear(); } glDisable(GL_CULL_FACE); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - - _heightfieldBaseBatches.clear(); } + _heightfieldBaseBatches.clear(); + _heightfieldSplatBatches.clear(); - if (!_voxelBaseBatches.isEmpty()) { + if (!_voxelBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { DependencyManager::get()->setPrimaryDrawBuffers(true, true); glEnableClientState(GL_VERTEX_ARRAY); @@ -472,17 +465,15 @@ void MetavoxelSystem::render() { _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materials); _splatVoxelProgram.disableAttributeArray(_splatVoxelLocations.materialWeights); - - _voxelSplatBatches.clear(); } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_CULL_FACE); - - _voxelBaseBatches.clear(); } + _voxelBaseBatches.clear(); + _voxelSplatBatches.clear(); if (!_hermiteBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { DependencyManager::get()->setPrimaryDrawBuffers(true, true); @@ -536,49 +527,6 @@ void MetavoxelSystem::refreshVoxelData() { }); } -class RayVoxelIntersectionVisitor : public RayIntersectionVisitor { -public: - - float intersectionDistance; - - RayVoxelIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info, float distance); -}; - -RayVoxelIntersectionVisitor::RayVoxelIntersectionVisitor(const glm::vec3& origin, - const glm::vec3& direction, const MetavoxelLOD& lod) : - RayIntersectionVisitor(origin, direction, QVector() << - Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), QVector(), lod), - intersectionDistance(FLT_MAX) { -} - -int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { - if (!info.isLeaf) { - return _order; - } - const VoxelBuffer* buffer = static_cast( - info.inputValues.at(0).getInlineValue().data()); - if (!buffer) { - return STOP_RECURSION; - } - glm::vec3 entry = ((_origin + distance * _direction) - info.minimum) / info.size; - if (buffer->findFirstRayIntersection(entry, _origin, _direction, intersectionDistance)) { - return SHORT_CIRCUIT; - } - return STOP_RECURSION; -} - -bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) { - RayVoxelIntersectionVisitor visitor(origin, direction, getLOD()); - guideToAugmented(visitor); - if (visitor.intersectionDistance == FLT_MAX) { - return false; - } - distance = visitor.intersectionDistance; - return true; -} - void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, SharedObjectPointer(), color)) }; @@ -681,75 +629,6 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - _heightfieldCursorProgram.bind(); - - glActiveTexture(GL_TEXTURE4); - float scale = 1.0f / radius; - glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); - glm::vec4 tCoefficients(0.0f, 0.0f, scale, -scale * position.z); - glm::vec4 rCoefficients(0.0f, 0.0f, 0.0f, 0.0f); - glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients); - glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients); - glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients); - glActiveTexture(GL_TEXTURE0); - - glm::vec3 extents(radius, radius, radius); - SpannerCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents)); - guide(visitor); - - _heightfieldCursorProgram.release(); - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_CULL_FACE); - glDepthFunc(GL_LESS); -} - -class BufferCursorRenderVisitor : public MetavoxelVisitor { -public: - - BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds); - - virtual int visit(MetavoxelInfo& info); - -private: - - Box _bounds; -}; - -BufferCursorRenderVisitor::BufferCursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) : - MetavoxelVisitor(QVector() << attribute), - _bounds(bounds) { -} - -int BufferCursorRenderVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - BufferData* buffer = info.inputValues.at(0).getInlineValue().data(); - if (buffer) { - buffer->render(glm::vec3(), glm::quat(), glm::vec3(1.0f, 1.0f, 1.0f), true); - } - return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; -} - -void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) { - glDepthFunc(GL_LEQUAL); - glEnable(GL_CULL_FACE); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, -1.0f); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glEnableClientState(GL_VERTEX_ARRAY); - - _voxelCursorProgram.bind(); - glActiveTexture(GL_TEXTURE4); float scale = 1.0f / radius; glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x); @@ -761,24 +640,79 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - Box bounds(position - extents, position + extents); - BufferCursorRenderVisitor voxelVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), bounds); - guideToAugmented(voxelVisitor); + SpannerCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents)); + guide(visitor); - _voxelCursorProgram.release(); + if (!_heightfieldBaseBatches.isEmpty()) { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + _heightfieldCursorProgram.bind(); - _heightfieldCursorProgram.bind(); + foreach (const HeightfieldBaseLayerBatch& batch, _heightfieldBaseBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + HeightfieldPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); + glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); + + glBindTexture(GL_TEXTURE_2D, batch.heightTextureID); + + glDrawRangeElements(GL_TRIANGLES, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + + glPopMatrix(); + } + + _heightfieldCursorProgram.release(); - SpannerCursorRenderVisitor spannerVisitor(getLOD(), bounds); - guide(spannerVisitor); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + } + _heightfieldBaseBatches.clear(); - _heightfieldCursorProgram.release(); + if (!_voxelBaseBatches.isEmpty()) { + glEnableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + _voxelCursorProgram.bind(); - glDisableClientState(GL_VERTEX_ARRAY); + foreach (const MetavoxelBatch& batch, _voxelBaseBatches) { + glPushMatrix(); + glTranslatef(batch.translation.x, batch.translation.y, batch.translation.z); + glm::vec3 axis = glm::axis(batch.rotation); + glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); + glScalef(batch.scale.x, batch.scale.y, batch.scale.z); + + batch.vertexBuffer->bind(); + batch.indexBuffer->bind(); + + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + + glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); + + batch.vertexBuffer->release(); + batch.indexBuffer->release(); + + glPopMatrix(); + } + + _voxelCursorProgram.release(); + + glDisableClientState(GL_VERTEX_ARRAY); + } + _voxelBaseBatches.clear(); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_CULL_FACE); @@ -1033,14 +967,6 @@ void Augmenter::run() { QMetaObject::invokeMethod(node->getLinkedData(), "setAugmentedData", Q_ARG(const MetavoxelData&, _data)); } -void MetavoxelSystemClient::refreshVoxelData() { - // make it look as if all the colors have changed - MetavoxelData oldData = getAugmentedData(); - oldData.touch(AttributeRegistry::getInstance()->getVoxelColorAttribute()); - - QThreadPool::globalInstance()->start(new Augmenter(_node, _data, oldData, _remoteDataLOD)); -} - void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { MetavoxelClient::dataChanged(oldData); QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); @@ -1210,22 +1136,6 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation } } - if (cursor) { - _vertexBuffer.bind(); - _indexBuffer.bind(); - - VoxelPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); - glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); - glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); - - _vertexBuffer.release(); - _indexBuffer.release(); - return; - } - MetavoxelBatch baseBatch; baseBatch.translation = translation; baseBatch.rotation = rotation; @@ -1236,7 +1146,7 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation baseBatch.indexCount = _indexCount; Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); - if (!_materials.isEmpty()) { + if (!(cursor || _materials.isEmpty())) { VoxelSplatBatch splatBatch; splatBatch.translation = translation; splatBatch.rotation = rotation; @@ -1291,650 +1201,9 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation } } -BufferDataAttribute::BufferDataAttribute(const QString& name) : - InlineAttribute(name) { -} - -bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const { - *(BufferDataPointer*)&parent = _defaultValue; - for (int i = 0; i < MERGE_COUNT; i++) { - if (decodeInline(children[i])) { - return false; - } - } - return true; -} - -AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const { - return AttributeValue(parentValue.getAttribute()); -} - DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } -class VoxelAugmentVisitor : public MetavoxelVisitor { -public: - - VoxelAugmentVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); -}; - -VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << - AttributeRegistry::getInstance()->getVoxelHermiteAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { -} - -class EdgeCrossing { -public: - glm::vec3 point; - glm::vec3 normal; - QRgb color; - char material; - - void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } -}; - -const int MAX_NORMALS_PER_VERTEX = 4; - -class NormalIndex { -public: - int indices[MAX_NORMALS_PER_VERTEX]; - - bool isValid() const; - - int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; -}; - -bool NormalIndex::isValid() const { - for (int i = 0; i < MAX_NORMALS_PER_VERTEX; i++) { - if (indices[i] != 0) { - return true; - } - } - return false; -} - -int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { - int firstIndex = indices[0]; - int closestIndex = firstIndex; - const VoxelPoint& firstVertex = vertices.at(firstIndex); - float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; - for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { - int index = indices[i]; - if (index == firstIndex) { - break; - } - const VoxelPoint& vertex = vertices.at(index); - float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; - if (product > closest) { - closest = product; - closestIndex = index; - } - } - return closestIndex; -} - -static glm::vec3 safeNormalize(const glm::vec3& vector) { - float length = glm::length(vector); - return (length > 0.0f) ? (vector / length) : vector; -} - -int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - BufferData* buffer = NULL; - VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue(); - VoxelMaterialDataPointer material = info.inputValues.at(1).getInlineValue(); - VoxelHermiteDataPointer hermite = info.inputValues.at(2).getInlineValue(); - - if (color && hermite) { - QVector vertices; - QVector indices; - QVector hermiteSegments; - QMultiHash quadIndices; - - // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the - // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges - const QVector& colorContents = color->getContents(); - const QVector& hermiteContents = hermite->getContents(); - int size = color->getSize(); - int area = size * size; - - // number variables such as offset3 and alpha0 in this function correspond to cube corners, where the x, y, and z - // components are represented as bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at - // the minimum x, y, and z corner and alpha7 is the value at the maximum x, y, and z - int offset3 = size + 1; - int offset5 = area + 1; - int offset6 = area + size; - int offset7 = area + size + 1; - - const QRgb* colorZ = colorContents.constData(); - const QRgb* hermiteData = hermiteContents.constData(); - int hermiteStride = hermite->getSize() * VoxelHermiteData::EDGE_COUNT; - int hermiteArea = hermiteStride * hermite->getSize(); - - const char* materialData = material ? material->getContents().constData() : NULL; - - // as we scan down the cube generating vertices between grid points, we remember the indices of the last - // (element, line, section--x, y, z) so that we can connect generated vertices as quads - int expanded = size + 1; - QVector lineIndices(expanded); - QVector lastLineIndices(expanded); - QVector planeIndices(expanded * expanded); - QVector lastPlaneIndices(expanded * expanded); - - const int EDGES_PER_CUBE = 12; - EdgeCrossing crossings[EDGES_PER_CUBE]; - - float highest = size - 1.0f; - float scale = info.size / highest; - const int ALPHA_OFFSET = 24; - bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); - for (int z = 0; z < expanded; z++) { - const QRgb* colorY = colorZ; - for (int y = 0; y < expanded; y++) { - NormalIndex lastIndex; - const QRgb* colorX = colorY; - for (int x = 0; x < expanded; x++) { - int alpha0 = colorX[0] >> ALPHA_OFFSET; - int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; - int alphaTotal = alpha0; - int possibleTotal = EIGHT_BIT_MAXIMUM; - - // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between - // neighboring blocks, which share only one layer of points - bool middleX = (x != 0 && x != size); - bool middleY = (y != 0 && y != size); - bool middleZ = (z != 0 && z != size); - if (middleZ) { - alphaTotal += (alpha4 = colorX[area] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - - int alpha5 = alpha4, alpha6 = alpha4; - if (middleY) { - alphaTotal += (alpha2 = colorX[size] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (middleZ) { - alphaTotal += (alpha6 = colorX[offset6] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - - int alpha3 = alpha2, alpha7 = alpha6; - if (middleX) { - alphaTotal += (alpha1 = colorX[1] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (middleY) { - alphaTotal += (alpha3 = colorX[offset3] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (middleZ) { - alphaTotal += (alpha7 = colorX[offset7] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - if (middleZ) { - alphaTotal += (alpha5 = colorX[offset5] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - if (alphaTotal == 0 || alphaTotal == possibleTotal) { - if (x != 0) { - colorX++; - } - continue; // no corners set/all corners set - } - // the terrifying conditional code that follows checks each cube edge for a crossing, gathering - // its properties (color, material, normal) if one is present; as before, boundary edges are excluded - int clampedX = qMax(x - 1, 0), clampedY = qMax(y - 1, 0), clampedZ = qMax(z - 1, 0); - const QRgb* hermiteBase = hermiteData + clampedZ * hermiteArea + clampedY * hermiteStride + - clampedX * VoxelHermiteData::EDGE_COUNT; - const char* materialBase = materialData ? - (materialData + clampedZ * area + clampedY * size + clampedX) : NULL; - int crossingCount = 0; - if (middleX) { - if (alpha0 != alpha1) { - QRgb hermite = hermiteBase[0]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha0 == 0) { - crossing.color = colorX[1]; - crossing.material = materialBase ? materialBase[1] : 0; - } else { - crossing.color = colorX[0]; - crossing.material = materialBase ? materialBase[0] : 0; - } - crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 0.0f); - } - if (middleY) { - if (alpha1 != alpha3) { - QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 1]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha1 == 0) { - crossing.color = colorX[offset3]; - crossing.material = materialBase ? materialBase[offset3] : 0; - } else { - crossing.color = colorX[1]; - crossing.material = materialBase ? materialBase[1] : 0; - } - crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - } - if (alpha2 != alpha3) { - QRgb hermite = hermiteBase[hermiteStride]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha2 == 0) { - crossing.color = colorX[offset3]; - crossing.material = materialBase ? materialBase[offset3] : 0; - } else { - crossing.color = colorX[size]; - crossing.material = materialBase ? materialBase[size] : 0; - } - crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 0.0f); - } - if (middleZ) { - if (alpha3 != alpha7) { - QRgb hermite = hermiteBase[hermiteStride + VoxelHermiteData::EDGE_COUNT + 2]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha3 == 0) { - crossing.color = colorX[offset7]; - crossing.material = materialBase ? materialBase[offset7] : 0; - } else { - crossing.color = colorX[offset3]; - crossing.material = materialBase ? materialBase[offset3] : 0; - } - crossing.point = glm::vec3(1.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - } - if (alpha5 != alpha7) { - QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha5 == 0) { - crossing.color = colorX[offset7]; - crossing.material = materialBase ? materialBase[offset7] : 0; - } else { - crossing.color = colorX[offset5]; - crossing.material = materialBase ? materialBase[offset5] : 0; - } - crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - } - if (alpha6 != alpha7) { - QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha6 == 0) { - crossing.color = colorX[offset7]; - crossing.material = materialBase ? materialBase[offset7] : 0; - } else { - crossing.color = colorX[offset6]; - crossing.material = materialBase ? materialBase[offset6] : 0; - } - crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 1.0f); - } - } - } - if (middleZ) { - if (alpha1 != alpha5) { - QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 2]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha1 == 0) { - crossing.color = colorX[offset5]; - crossing.material = materialBase ? materialBase[offset5] : 0; - } else { - crossing.color = colorX[1]; - crossing.material = materialBase ? materialBase[1] : 0; - } - crossing.point = glm::vec3(1.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - } - if (alpha4 != alpha5) { - QRgb hermite = hermiteBase[hermiteArea]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha4 == 0) { - crossing.color = colorX[offset5]; - crossing.material = materialBase ? materialBase[offset5] : 0; - } else { - crossing.color = colorX[area]; - crossing.material = materialBase ? materialBase[area] : 0; - } - crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 1.0f); - } - } - } - if (middleY) { - if (alpha0 != alpha2) { - QRgb hermite = hermiteBase[1]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha0 == 0) { - crossing.color = colorX[size]; - crossing.material = materialBase ? materialBase[size] : 0; - } else { - crossing.color = colorX[0]; - crossing.material = materialBase ? materialBase[0] : 0; - } - crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); - } - if (middleZ) { - if (alpha2 != alpha6) { - QRgb hermite = hermiteBase[hermiteStride + 2]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha2 == 0) { - crossing.color = colorX[offset6]; - crossing.material = materialBase ? materialBase[offset6] : 0; - } else { - crossing.color = colorX[size]; - crossing.material = materialBase ? materialBase[size] : 0; - } - crossing.point = glm::vec3(0.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - } - if (alpha4 != alpha6) { - QRgb hermite = hermiteBase[hermiteArea + 1]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha4 == 0) { - crossing.color = colorX[offset6]; - crossing.material = materialBase ? materialBase[offset6] : 0; - } else { - crossing.color = colorX[area]; - crossing.material = materialBase ? materialBase[area] : 0; - } - crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); - } - } - } - if (middleZ && alpha0 != alpha4) { - QRgb hermite = hermiteBase[2]; - EdgeCrossing& crossing = crossings[crossingCount++]; - crossing.normal = unpackNormal(hermite); - if (alpha0 == 0) { - crossing.color = colorX[area]; - crossing.material = materialBase ? materialBase[area] : 0; - } else { - crossing.color = colorX[0]; - crossing.material = materialBase ? materialBase[0] : 0; - } - crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); - } - // at present, we simply average the properties of each crossing as opposed to finding the vertex that - // minimizes the quadratic error function as described in the reference paper - glm::vec3 center; - glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; - int normalCount = 0; - const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); - const int MAX_MATERIALS_PER_VERTEX = 4; - quint8 materials[] = { 0, 0, 0, 0 }; - glm::vec4 materialWeights; - float totalWeight = 0.0f; - int red = 0, green = 0, blue = 0; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - center += crossing.point; - - int j = 0; - for (; j < normalCount; j++) { - if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { - normals[j] = safeNormalize(normals[j] + crossing.normal); - break; - } - } - if (j == normalCount) { - normals[normalCount++] = crossing.normal; - } - - red += qRed(crossing.color); - green += qGreen(crossing.color); - blue += qBlue(crossing.color); - - if (displayHermite) { - glm::vec3 start = info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + - crossing.point) * scale; - hermiteSegments.append(start); - hermiteSegments.append(start + crossing.normal * scale); - } - - // when assigning a material, search for its presence and, if not found, - // place it in the first empty slot - if (crossing.material != 0) { - for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { - if (materials[j] == crossing.material) { - materialWeights[j] += 1.0f; - totalWeight += 1.0f; - break; - - } else if (materials[j] == 0) { - materials[j] = crossing.material; - materialWeights[j] = 1.0f; - totalWeight += 1.0f; - break; - } - } - } - } - center /= crossingCount; - - // use a sequence of Givens rotations to perform a QR decomposition - // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf - glm::mat4 r(0.0f); - glm::vec4 bottom; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); - - for (int j = 0; j < 4; j++) { - float angle = glm::atan(-bottom[j], r[j][j]); - float sina = glm::sin(angle); - float cosa = glm::cos(angle); - - for (int k = 0; k < 4; k++) { - float tmp = bottom[k]; - bottom[k] = sina * r[k][j] + cosa * tmp; - r[k][j] = cosa * r[k][j] - sina * tmp; - } - } - } - - // extract the submatrices, form ata - glm::mat3 a(r); - glm::vec3 b(r[3]); - glm::mat3 atrans = glm::transpose(a); - glm::mat3 ata = atrans * a; - - // find the eigenvalues and eigenvectors of ata - // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) - glm::mat3 d = ata; - glm::quat combinedRotation; - const int MAX_ITERATIONS = 20; - for (int i = 0; i < MAX_ITERATIONS; i++) { - glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); - int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : - (offDiagonals[1] > offDiagonals[2] ? 1 : 2); - const float DESIRED_PRECISION = 0.00001f; - if (offDiagonals[largestIndex] < DESIRED_PRECISION) { - break; - } - int largestJ = (largestIndex == 2) ? 1 : 0; - int largestI = (largestIndex == 0) ? 1 : 2; - float sjj = d[largestJ][largestJ]; - float sii = d[largestI][largestI]; - float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; - glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : - (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); - combinedRotation = glm::normalize(rotation * combinedRotation); - glm::mat3 matrix = glm::mat3_cast(combinedRotation); - d = matrix * ata * glm::transpose(matrix); - } - - // form the singular matrix from the eigenvalues - const float MIN_SINGULAR_THRESHOLD = 0.1f; - d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; - d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; - d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; - - // compute the pseudo-inverse, ataplus, and use to find the minimizing solution - glm::mat3 u = glm::mat3_cast(combinedRotation); - glm::mat3 ataplus = glm::transpose(u) * d * u; - glm::vec3 solution = (ataplus * atrans * b) + center; - - // make sure it doesn't fall beyond the cell boundaries - center = glm::clamp(solution, 0.0f, 1.0f); - - if (totalWeight > 0.0f) { - materialWeights *= (EIGHT_BIT_MAXIMUM / totalWeight); - } - VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center) * scale, - { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, - { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, - { materials[0], materials[1], materials[2], materials[3] }, - { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], - (quint8)materialWeights[3] } }; - - NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; - vertices.append(point); - for (int i = 1; i < normalCount; i++) { - index.indices[i] = vertices.size(); - point.setNormal(normals[i]); - vertices.append(point); - } - - // the first x, y, and z are repeated for the boundary edge; past that, we consider generating - // quads for each edge that includes a transition, using indices of previously generated vertices - if (x != 0 && y != 0 && z != 0) { - if (alpha0 != alpha1) { - quadIndices.insert(qRgb(x, y, z), indices.size()); - quadIndices.insert(qRgb(x, y - 1, z), indices.size()); - quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); - quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - - const NormalIndex& index1 = lastLineIndices.at(x); - const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); - const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative x - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive x - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - - if (alpha0 != alpha2) { - quadIndices.insert(qRgb(x, y, z), indices.size()); - quadIndices.insert(qRgb(x - 1, y, z), indices.size()); - quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); - quadIndices.insert(qRgb(x, y, z - 1), indices.size()); - - const NormalIndex& index1 = lastIndex; - const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); - const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, - vertices.at(index1.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative y - indices.append(index1.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } else { // quad faces positive y - indices.append(index3.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - - if (alpha0 != alpha4) { - quadIndices.insert(qRgb(x, y, z), indices.size()); - quadIndices.insert(qRgb(x - 1, y, z), indices.size()); - quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); - quadIndices.insert(qRgb(x, y - 1, z), indices.size()); - - const NormalIndex& index1 = lastIndex; - const NormalIndex& index2 = lastLineIndices.at(x - 1); - const NormalIndex& index3 = lastLineIndices.at(x); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative z - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive z - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); - } - } - lastIndex = index; - lineIndices[x] = index; - planeIndices[y * expanded + x] = index; - - if (x != 0) { - colorX++; - } - } - lineIndices.swap(lastLineIndices); - - if (y != 0) { - colorY += size; - } - } - planeIndices.swap(lastPlaneIndices); - - if (z != 0) { - colorZ += area; - } - } - buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size, - material ? material->getMaterials() : QVector()); - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return STOP_RECURSION; -} - -void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, - MetavoxelInfo& info, const MetavoxelLOD& lod) { - // copy the previous buffers - MetavoxelData expandedPrevious = previous; - while (expandedPrevious.getSize() < data.getSize()) { - expandedPrevious.expand(); - } - const AttributePointer& voxelBufferAttribute = - Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); - MetavoxelNode* root = expandedPrevious.getRoot(voxelBufferAttribute); - if (root) { - data.setRoot(voxelBufferAttribute, root); - root->incrementReferenceCount(); - } - VoxelAugmentVisitor voxelAugmentVisitor(lod); - data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); -} - class SpannerSimulateVisitor : public SpannerVisitor { public: @@ -1964,59 +1233,14 @@ void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float data.guide(spannerSimulateVisitor); } -class BufferRenderVisitor : public MetavoxelVisitor { -public: - - BufferRenderVisitor(const AttributePointer& attribute); - - virtual int visit(MetavoxelInfo& info); - -private: - - int _order; - int _containmentDepth; -}; - -BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : - MetavoxelVisitor(QVector() << attribute), - _order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), - _containmentDepth(INT_MAX) { -} - -int BufferRenderVisitor::visit(MetavoxelInfo& info) { - if (_containmentDepth >= _depth) { - Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( - info.getBounds()); - if (intersection == Frustum::NO_INTERSECTION) { - return STOP_RECURSION; - } - _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; - } - if (!info.isLeaf) { - return _order; - } - BufferDataPointer buffer = info.inputValues.at(0).getInlineValue(); - if (buffer) { - buffer->render(glm::vec3(), glm::quat(), glm::vec3(1.0f, 1.0f, 1.0f)); - } - return STOP_RECURSION; -} - void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSpanners)) { - SpannerRenderVisitor spannerRenderVisitor(lod); - data.guide(spannerRenderVisitor); - } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { - BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); - data.guide(voxelRenderVisitor); - } + SpannerRenderVisitor spannerRenderVisitor(lod); + data.guide(spannerRenderVisitor); } SphereRenderer::SphereRenderer() { } - void SphereRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Sphere* sphere = static_cast(_spanner); const QColor& color = sphere->getColor(); @@ -2173,6 +1397,61 @@ HeightfieldNodeRenderer::~HeightfieldNodeRenderer() { Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); } +class EdgeCrossing { +public: + glm::vec3 point; + glm::vec3 normal; + QRgb color; + char material; + + void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } +}; + +const int MAX_NORMALS_PER_VERTEX = 4; + +class NormalIndex { +public: + int indices[MAX_NORMALS_PER_VERTEX]; + + bool isValid() const; + + int getClosestIndex(const glm::vec3& normal, QVector& vertices) const; +}; + +bool NormalIndex::isValid() const { + for (int i = 0; i < MAX_NORMALS_PER_VERTEX; i++) { + if (indices[i] != 0) { + return true; + } + } + return false; +} + +int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector& vertices) const { + int firstIndex = indices[0]; + int closestIndex = firstIndex; + const VoxelPoint& firstVertex = vertices.at(firstIndex); + float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; + for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { + int index = indices[i]; + if (index == firstIndex) { + break; + } + const VoxelPoint& vertex = vertices.at(index); + float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; + if (product > closest) { + closest = product; + closestIndex = index; + } + } + return closestIndex; +} + +static glm::vec3 safeNormalize(const glm::vec3& vector) { + float length = glm::length(vector); + return (length > 0.0f) ? (vector / length) : vector; +} + class IndexVector : public QVector { public: @@ -2314,6 +1593,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); if ((!_voxels || (displayHermite && !static_cast(_voxels.data())->isHermiteEnabled())) && node->getStack()) { + // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the + // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges + QVector vertices; QVector indices; QVector hermiteSegments; @@ -2327,6 +1609,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; + // as we scan down the cube generating vertices between grid points, we remember the indices of the last + // (element, line, section--x, y, z) so that we can connect generated vertices as quads IndexVector indicesX; IndexVector lastIndicesX; QVector indicesZ(stackWidth + 1); @@ -2381,6 +1665,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g hermiteSegments.append(start + normal * scale); } } + // number variables correspond to cube corners, where the x, y, and z components are represented as + // bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at the minimum x, y, and + // z corner and alpha7 is the value at the maximum x, y, and z int alpha0 = qAlpha(entry.color); int alpha2 = lineSrc->getEntryAlpha(y + 1); int alpha1 = alpha0, alpha3 = alpha2, alpha4 = alpha0, alpha6 = alpha2; @@ -2751,32 +2038,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g _voxels->render(translation, rotation, glm::vec3(scale.x, scale.x, scale.x), cursor); } - if (cursor) { - bufferPair.first.bind(); - bufferPair.second.bind(); - - glPushMatrix(); - glTranslatef(translation.x, translation.y, translation.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(scale.x, scale.y, scale.z); - - HeightfieldPoint* point = 0; - glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); - glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord); - - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - - glPopMatrix(); - - bufferPair.first.release(); - bufferPair.second.release(); - return; - } HeightfieldBaseLayerBatch baseBatch; baseBatch.vertexBuffer = &bufferPair.first; baseBatch.indexBuffer = &bufferPair.second; @@ -2791,7 +2052,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); - if (!_networkTextures.isEmpty()) { + if (!(cursor || _networkTextures.isEmpty())) { HeightfieldSplatBatch splatBatch; splatBatch.vertexBuffer = &bufferPair.first; splatBatch.indexBuffer = &bufferPair.second; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 1a43c36464..45728901a5 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -59,18 +59,11 @@ public: void setNetworkSimulation(const NetworkSimulation& simulation); NetworkSimulation getNetworkSimulation(); - const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } - const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } - void simulate(float deltaTime); void render(); void renderHeightfieldCursor(const glm::vec3& position, float radius); - void renderVoxelCursor(const glm::vec3& position, float radius); - - bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance); - Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color); Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); @@ -107,9 +100,6 @@ private: void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); - AttributePointer _heightfieldBufferAttribute; - AttributePointer _voxelBufferAttribute; - MetavoxelLOD _lod; QReadWriteLock _lodLock; Frustum _frustum; @@ -267,8 +257,6 @@ public: void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; } virtual int parseData(const QByteArray& packet); - - Q_INVOKABLE void refreshVoxelData(); protected: @@ -360,19 +348,6 @@ private: QVector _networkTextures; }; -/// A client-side attribute that stores renderable buffers. -class BufferDataAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE BufferDataAttribute(const QString& name = QString()); - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; - - virtual AttributeValue inherit(const AttributeValue& parentValue) const; -}; - /// Renders metavoxels as points. class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation { Q_OBJECT @@ -381,7 +356,6 @@ public: Q_INVOKABLE DefaultMetavoxelRendererImplementation(); - virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod); virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod); }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b10d162b34..62305de6db 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -20,13 +20,9 @@ #include "Spanner.h" REGISTER_META_OBJECT(FloatAttribute) -REGISTER_META_OBJECT(MaterialObject) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) -REGISTER_META_OBJECT(VoxelColorAttribute) -REGISTER_META_OBJECT(VoxelHermiteAttribute) -REGISTER_META_OBJECT(VoxelMaterialAttribute) static int attributePointerMetaTypeId = qRegisterMetaType(); static int ownedAttributeValueMetaTypeId = qRegisterMetaType(); @@ -41,20 +37,12 @@ AttributeRegistry::AttributeRegistry() : new DefaultMetavoxelGuide()))), _rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject, new DefaultMetavoxelRenderer()))), - _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))), - _voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))), - _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))), - _voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) { + _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) { - // our baseline LOD threshold is for voxels; spanners and heightfields are a different story + // our baseline LOD threshold is for voxels; spanners are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); _spannersAttribute->setUserFacing(true); - - const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f; - _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); - _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); - _voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -274,846 +262,6 @@ FloatAttribute::FloatAttribute(const QString& name) : SimpleInlineAttribute(name) { } -const float CHAR_SCALE = 127.0f; -const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE; - -QRgb packNormal(const glm::vec3& normal) { - return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE)); -} - -QRgb packNormal(const glm::vec3& normal, int alpha) { - return qRgba((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE), alpha); -} - -glm::vec3 unpackNormal(QRgb value) { - return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE, - (char)qBlue(value) * INVERSE_CHAR_SCALE); -} - -DataBlock::~DataBlock() { -} - -MaterialObject::MaterialObject() : - _scaleS(1.0f), - _scaleT(1.0f) { -} - -static QHash countIndices(const QByteArray& contents) { - QHash counts; - for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { - if (*src != 0) { - counts[*src]++; - } - } - return counts; -} - -const float EIGHT_BIT_MAXIMUM = 255.0f; - -uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { - if (!(material && static_cast(material.data())->getDiffuse().isValid())) { - return 0; - } - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& existingMaterial = materials.at(i); - if (existingMaterial) { - if (existingMaterial->equals(material.data())) { - return i + 1; - } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; - } - } - // if nothing found, use the first empty slot or append - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = material; - return firstEmptyIndex + 1; - } - if (materials.size() < EIGHT_BIT_MAXIMUM) { - materials.append(material); - return materials.size(); - } - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - uchar materialIndex = 0; - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); - } - } - contents.replace((char)materialIndex, (char)0); - return materialIndex; -} - -void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); - } - } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } -} - -const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; - -static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, - int sizeX, int sizeY, int sizeZ, const QVector& contents) { - QByteArray inflated(VOXEL_COLOR_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = offsetZ; - *header++ = sizeX; - *header++ = sizeY; - *header++ = sizeZ; - inflated.append((const char*)contents.constData(), contents.size() * sizeof(QRgb)); - return qCompress(inflated); -} - -static QVector decodeVoxelColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ, - int& sizeX, int& sizeY, int& sizeZ) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - offsetZ = *header++; - sizeX = *header++; - sizeY = *header++; - sizeZ = *header++; - int payloadSize = inflated.size() - VOXEL_COLOR_HEADER_SIZE; - QVector contents(payloadSize / sizeof(QRgb)); - memcpy(contents.data(), inflated.constData() + VOXEL_COLOR_HEADER_SIZE, payloadSize); - return contents; -} - -VoxelColorData::VoxelColorData(const QVector& contents, int size) : - _contents(contents), - _size(size) { -} - -VoxelColorData::VoxelColorData(Bitstream& in, int bytes) { - read(in, bytes); -} - -VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - _size = reference->getSize(); - - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - QVector delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _size = sizeX; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - int minZ = offsetZ - 1; - const QRgb* src = delta.constData(); - int size2 = _size * _size; - QRgb* planeDest = _contents.data() + minZ * size2 + minY * _size + minX; - int length = sizeX * sizeof(QRgb); - for (int z = 0; z < sizeZ; z++, planeDest += size2) { - QRgb* dest = planeDest; - for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) { - memcpy(dest, src, length); - } - } -} - -void VoxelColorData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) { - if (!reference || reference->getSize() != _size) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int minX = _size, minY = _size, minZ = _size; - int maxX = -1, maxY = -1, maxZ = -1; - const QRgb* src = _contents.constData(); - const QRgb* ref = reference->getContents().constData(); - for (int z = 0; z < _size; z++) { - bool differenceZ = false; - for (int y = 0; y < _size; y++) { - bool differenceY = false; - for (int x = 0; x < _size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - differenceY = differenceZ = true; - } - } - if (differenceY) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (differenceZ) { - minZ = qMin(minZ, z); - maxZ = qMax(maxZ, z); - } - } - QVector delta; - int sizeX = 0, sizeY = 0, sizeZ = 0; - if (maxX >= minX) { - sizeX = maxX - minX + 1; - sizeY = maxY - minY + 1; - sizeZ = maxZ - minZ + 1; - delta = QVector(sizeX * sizeY * sizeZ, 0); - QRgb* dest = delta.data(); - int size2 = _size * _size; - const QRgb* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX; - int length = sizeX * sizeof(QRgb); - for (int z = 0; z < sizeZ; z++, planeSrc += size2) { - src = planeSrc; - for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) { - memcpy(dest, src, length); - } - } - } - reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void VoxelColorData::read(Bitstream& in, int bytes) { - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - _contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - _size = sizeX; -} - -VoxelColorAttribute::VoxelColorAttribute(const QString& name) : - InlineAttribute(name) { -} - -void VoxelColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(); - } else { - *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(in, size)); - } -} - -void VoxelColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelColorDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void VoxelColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(); - } else { - *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData( - in, size, decodeInline(reference))); - } -} - -void VoxelColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelColorDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool VoxelColorAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - VoxelColorDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getSize()); - } - } - if (maxSize == 0) { - *(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(); - return true; - } - int size = maxSize; - int area = size * size; - QVector contents(area * size); - int halfSize = size / 2; - int halfSizeComplement = size - halfSize; - for (int i = 0; i < MERGE_COUNT; i++) { - VoxelColorDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QVector& childContents = child->getContents(); - int childSize = child->getSize(); - int childArea = childSize * childSize; - const int INDEX_MASK = 1; - int xIndex = i & INDEX_MASK; - const int Y_SHIFT = 1; - int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - int Z_SHIFT = 2; - int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - QRgb* dest = contents.data() + (zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize); - const QRgb* src = childContents.data(); - - const int MAX_ALPHA = 255; - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSizeComplement; z++) { - int offset4 = (z == halfSize) ? 0 : childArea; - for (int y = 0; y < halfSizeComplement; y++) { - int offset2 = (y == halfSize) ? 0 : childSize; - int offset6 = offset4 + offset2; - for (QRgb* end = dest + halfSizeComplement; dest != end; ) { - int offset1 = (dest == end - 1) ? 0 : 1; - QRgb v0 = src[0], v1 = src[offset1], v2 = src[offset2], v3 = src[offset2 + offset1], v4 = src[offset4], - v5 = src[offset4 + offset1], v6 = src[offset6], v7 = src[offset6 + offset1]; - src += (1 + offset1); - int a0 = qAlpha(v0), a1 = qAlpha(v1), a2 = qAlpha(v2), a3 = qAlpha(v3), - a4 = qAlpha(v4), a5 = qAlpha(v5), a6 = qAlpha(v6), a7 = qAlpha(v7); - if (a0 == 0) { - *dest++ = qRgba(0, 0, 0, 0); - continue; - } - int alphaTotal = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7; - *dest++ = qRgba( - (qRed(v0) * a0 + qRed(v1) * a1 + qRed(v2) * a2 + qRed(v3) * a3 + - qRed(v4) * a4 + qRed(v5) * a5 + qRed(v6) * a6 + qRed(v7) * a7) / alphaTotal, - (qGreen(v0) * a0 + qGreen(v1) * a1 + qGreen(v2) * a2 + qGreen(v3) * a3 + - qGreen(v4) * a4 + qGreen(v5) * a5 + qGreen(v6) * a6 + qGreen(v7) * a7) / alphaTotal, - (qBlue(v0) * a0 + qBlue(v1) * a1 + qBlue(v2) * a2 + qBlue(v3) * a3 + - qBlue(v4) * a4 + qBlue(v5) * a5 + qBlue(v6) * a6 + qBlue(v7) * a7) / alphaTotal, - MAX_ALPHA); - } - dest += halfSize; - src += offset2; - } - dest += halfSize * size; - src += offset4; - } - } else { - // more complex: N destination values for four child values - // ... - } - } - *(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(new VoxelColorData(contents, size)); - return false; -} - -const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6; - -static QByteArray encodeVoxelMaterial(int offsetX, int offsetY, int offsetZ, - int sizeX, int sizeY, int sizeZ, const QByteArray& contents) { - QByteArray inflated(VOXEL_MATERIAL_HEADER_SIZE, 0); - qint32* header = (qint32*)inflated.data(); - *header++ = offsetX; - *header++ = offsetY; - *header++ = offsetZ; - *header++ = sizeX; - *header++ = sizeY; - *header++ = sizeZ; - inflated.append(contents); - return qCompress(inflated); -} - -static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ, - int& sizeX, int& sizeY, int& sizeZ) { - QByteArray inflated = qUncompress(encoded); - const qint32* header = (const qint32*)inflated.constData(); - offsetX = *header++; - offsetY = *header++; - offsetZ = *header++; - sizeX = *header++; - sizeY = *header++; - sizeZ = *header++; - return inflated.mid(VOXEL_MATERIAL_HEADER_SIZE); -} - -VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector& materials) : - _contents(contents), - _size(size), - _materials(materials) { -} - -VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) { - read(in, bytes); -} - -VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_materials, reference->getMaterials()); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - _size = reference->getSize(); - - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - QByteArray delta = decodeVoxelMaterial(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _size = sizeX; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - int minZ = offsetZ - 1; - const char* src = delta.constData(); - int size2 = _size * _size; - char* planeDest = _contents.data() + minZ * size2 + minY * _size + minX; - for (int z = 0; z < sizeZ; z++, planeDest += size2) { - char* dest = planeDest; - for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) { - memcpy(dest, src, sizeX); - } - } -} - -void VoxelMaterialData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeVoxelMaterial(0, 0, 0, _size, _size, _size, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); - out << _materials; -} - -void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) { - if (!reference || reference->getSize() != _size) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int minX = _size, minY = _size, minZ = _size; - int maxX = -1, maxY = -1, maxZ = -1; - const char* src = _contents.constData(); - const char* ref = reference->getContents().constData(); - for (int z = 0; z < _size; z++) { - bool differenceZ = false; - for (int y = 0; y < _size; y++) { - bool differenceY = false; - for (int x = 0; x < _size; x++) { - if (*src++ != *ref++) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - differenceY = differenceZ = true; - } - } - if (differenceY) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (differenceZ) { - minZ = qMin(minZ, z); - maxZ = qMax(maxZ, z); - } - } - QByteArray delta; - int sizeX = 0, sizeY = 0, sizeZ = 0; - if (maxX >= minX) { - sizeX = maxX - minX + 1; - sizeY = maxY - minY + 1; - sizeZ = maxZ - minZ + 1; - delta = QByteArray(sizeX * sizeY * sizeZ, 0); - char* dest = delta.data(); - int size2 = _size * _size; - const char* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX; - for (int z = 0; z < sizeZ; z++, planeSrc += size2) { - src = planeSrc; - for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) { - memcpy(dest, src, sizeX); - } - } - } - reference->setEncodedDelta(encodeVoxelMaterial(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_materials, reference->getMaterials()); -} - -void VoxelMaterialData::read(Bitstream& in, int bytes) { - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - _contents = decodeVoxelMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - _size = sizeX; - in >> _materials; -} - -VoxelMaterialAttribute::VoxelMaterialAttribute(const QString& name) : - InlineAttribute(name) { -} - -void VoxelMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(); - } else { - *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(in, size)); - } -} - -void VoxelMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelMaterialDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void VoxelMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(); - } else { - *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData( - in, size, decodeInline(reference))); - } -} - -void VoxelMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelMaterialDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - VoxelMaterialDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getSize()); - } - } - *(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer(); - return maxSize == 0; -} - -VoxelHermiteData::VoxelHermiteData(const QVector& contents, int size) : - _contents(contents), - _size(size) { -} - -VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) { - read(in, bytes); -} - -VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) { - if (!reference) { - read(in, bytes); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(DataBlockPointer(this)); - _contents = reference->getContents(); - _size = reference->getSize(); - - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - QVector delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - if (delta.isEmpty()) { - return; - } - if (offsetX == 0) { - _contents = delta; - _size = sizeX; - return; - } - int minX = offsetX - 1; - int minY = offsetY - 1; - int minZ = offsetZ - 1; - const QRgb* src = delta.constData(); - int destStride = _size * EDGE_COUNT; - int destStride2 = _size * destStride; - QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT; - int srcStride = sizeX * EDGE_COUNT; - int length = srcStride * sizeof(QRgb); - for (int z = 0; z < sizeZ; z++, planeDest += destStride2) { - QRgb* dest = planeDest; - for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, length); - } - } -} - -void VoxelHermiteData::write(Bitstream& out) { - QMutexLocker locker(&_encodedMutex); - if (_encoded.isEmpty()) { - _encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents); - } - out << _encoded.size(); - out.writeAligned(_encoded); -} - -void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) { - if (!reference || reference->getSize() != _size) { - write(out); - return; - } - QMutexLocker locker(&reference->getEncodedDeltaMutex()); - if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - int minX = _size, minY = _size, minZ = _size; - int maxX = -1, maxY = -1, maxZ = -1; - const QRgb* src = _contents.constData(); - const QRgb* ref = reference->getContents().constData(); - for (int z = 0; z < _size; z++) { - bool differenceZ = false; - for (int y = 0; y < _size; y++) { - bool differenceY = false; - for (int x = 0; x < _size; x++, src += EDGE_COUNT, ref += EDGE_COUNT) { - if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { - minX = qMin(minX, x); - maxX = qMax(maxX, x); - differenceY = differenceZ = true; - } - } - if (differenceY) { - minY = qMin(minY, y); - maxY = qMax(maxY, y); - } - } - if (differenceZ) { - minZ = qMin(minZ, z); - maxZ = qMax(maxZ, z); - } - } - QVector delta; - int sizeX = 0, sizeY = 0, sizeZ = 0; - if (maxX >= minX) { - sizeX = maxX - minX + 1; - sizeY = maxY - minY + 1; - sizeZ = maxZ - minZ + 1; - delta = QVector(sizeX * sizeY * sizeZ * EDGE_COUNT, 0); - QRgb* dest = delta.data(); - int srcStride = _size * EDGE_COUNT; - int srcStride2 = _size * srcStride; - const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT; - int destStride = sizeX * EDGE_COUNT; - int length = destStride * sizeof(QRgb); - for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) { - src = planeSrc; - for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, length); - } - } - } - reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); - reference->setDeltaData(DataBlockPointer(this)); - } - out << reference->getEncodedDelta().size(); - out.writeAligned(reference->getEncodedDelta()); -} - -void VoxelHermiteData::read(Bitstream& in, int bytes) { - int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; - _contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); - _size = sizeX; -} - -VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) : - InlineAttribute(name) { -} - -void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(); - } else { - *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size)); - } -} - -void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelHermiteDataPointer data = decodeInline(value); - if (data) { - data->write(out); - } else { - out << 0; - } -} - -void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - int size; - in >> size; - if (size == 0) { - *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(); - } else { - *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData( - in, size, decodeInline(reference))); - } -} - -void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { - if (!isLeaf) { - return; - } - VoxelHermiteDataPointer data = decodeInline(value); - if (data) { - data->writeDelta(out, decodeInline(reference)); - } else { - out << 0; - } -} - -bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const { - int maxSize = 0; - for (int i = 0; i < MERGE_COUNT; i++) { - VoxelHermiteDataPointer pointer = decodeInline(children[i]); - if (pointer) { - maxSize = qMax(maxSize, pointer->getSize()); - } - } - if (maxSize == 0) { - *(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(); - return true; - } - int size = maxSize; - int area = size * size; - QVector contents(area * size * VoxelHermiteData::EDGE_COUNT); - int halfSize = size / 2; - int halfSizeComplement = size - halfSize; - for (int i = 0; i < MERGE_COUNT; i++) { - VoxelHermiteDataPointer child = decodeInline(children[i]); - if (!child) { - continue; - } - const QVector& childContents = child->getContents(); - int childSize = child->getSize(); - int childArea = childSize * childSize; - const int INDEX_MASK = 1; - int xIndex = i & INDEX_MASK; - const int Y_SHIFT = 1; - int yIndex = (i >> Y_SHIFT) & INDEX_MASK; - int Z_SHIFT = 2; - int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - QRgb* dest = contents.data() + ((zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize)) * - VoxelHermiteData::EDGE_COUNT; - const QRgb* src = childContents.data(); - int offsets[VoxelHermiteData::EDGE_COUNT]; - - if (childSize == size) { - // simple case: one destination value for four child values - for (int z = 0; z < halfSizeComplement; z++) { - offsets[2] = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT); - for (int y = 0; y < halfSizeComplement; y++) { - offsets[1] = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT); - for (QRgb* end = dest + halfSizeComplement * VoxelHermiteData::EDGE_COUNT; dest != end; - dest += VoxelHermiteData::EDGE_COUNT) { - offsets[0] = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT; - for (int i = 0; i < VoxelHermiteData::EDGE_COUNT; i++) { - QRgb v0 = src[i], v1 = src[i + offsets[i]]; - glm::vec3 n0 = unpackNormal(v0), n1 = unpackNormal(v1); - float l0 = glm::length(n0), l1 = glm::length(n1); - float lengthTotal = l0 + l1; - if (lengthTotal == 0.0f) { - dest[i] = qRgba(0, 0, 0, 0); - continue; - } - glm::vec3 combinedNormal = n0 + n1; - float combinedLength = glm::length(combinedNormal); - if (combinedLength > 0.0f) { - combinedNormal /= combinedLength; - } - float combinedOffset = qAlpha(v0) * 0.5f * l0 + (qAlpha(v1) + EIGHT_BIT_MAXIMUM) * 0.5f * l1; - dest[i] = packNormal(combinedNormal, combinedOffset / lengthTotal); - } - src += (VoxelHermiteData::EDGE_COUNT + offsets[0]); - } - dest += (halfSize * VoxelHermiteData::EDGE_COUNT); - src += offsets[1]; - } - dest += (halfSize * size * VoxelHermiteData::EDGE_COUNT); - src += offsets[2]; - } - } else { - // more complex: N destination values for four child values - // ... - } - } - *(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(new VoxelHermiteData(contents, size)); - return false; -} - SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 50814ac912..ea0ec263e4 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -30,16 +30,10 @@ class QScriptValue; class Attribute; class DataBlock; -class HeightfieldColorData; -class HeightfieldHeightData; -class HeightfieldMaterialData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; class MetavoxelStreamState; -class VoxelColorData; -class VoxelHermiteData; -class VoxelMaterialData; typedef SharedObjectPointerTemplate AttributePointer; @@ -88,24 +82,6 @@ public: /// Returns a reference to the standard SharedObjectSet "spanners" attribute. const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; } - /// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute. - const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } - - /// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute. - const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; } - - /// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute. - const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; } - - /// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute. - const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; } - - /// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute. - const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; } - - /// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute. - const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; } - private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -116,12 +92,6 @@ private: AttributePointer _guideAttribute; AttributePointer _rendererAttribute; AttributePointer _spannersAttribute; - AttributePointer _heightfieldAttribute; - AttributePointer _heightfieldColorAttribute; - AttributePointer _heightfieldMaterialAttribute; - AttributePointer _voxelColorAttribute; - AttributePointer _voxelMaterialAttribute; - AttributePointer _voxelHermiteAttribute; }; /// Converts a value to a void pointer. @@ -355,213 +325,6 @@ public: Q_INVOKABLE FloatAttribute(const QString& name = QString()); }; -/// Packs a normal into an RGB value. -QRgb packNormal(const glm::vec3& normal); - -/// Packs a normal (plus extra alpha value) into an RGBA value. -QRgb packNormal(const glm::vec3& normal, int alpha); - -/// Unpacks a normal from an RGB value. -glm::vec3 unpackNormal(QRgb value); - -typedef QExplicitlySharedDataPointer DataBlockPointer; - -/// Base class for blocks of data. -class DataBlock : public QSharedData { -public: - - static const int COLOR_BYTES = 3; - - virtual ~DataBlock(); - - void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; } - const DataBlockPointer& getDeltaData() const { return _deltaData; } - - void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; } - const QByteArray& getEncodedDelta() const { return _encodedDelta; } - - QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; } - -protected: - - QByteArray _encoded; - QMutex _encodedMutex; - - DataBlockPointer _deltaData; - QByteArray _encodedDelta; - QMutex _encodedDeltaMutex; - - class EncodedSubdivision { - public: - DataBlockPointer ancestor; - QByteArray data; - }; - QVector _encodedSubdivisions; - QMutex _encodedSubdivisionsMutex; -}; - -/// Contains the description of a material. -class MaterialObject : public SharedObject { - Q_OBJECT - Q_PROPERTY(QUrl diffuse MEMBER _diffuse) - Q_PROPERTY(float scaleS MEMBER _scaleS) - Q_PROPERTY(float scaleT MEMBER _scaleT) - -public: - - Q_INVOKABLE MaterialObject(); - - const QUrl& getDiffuse() const { return _diffuse; } - - float getScaleS() const { return _scaleS; } - float getScaleT() const { return _scaleT; } - -private: - - QUrl _diffuse; - float _scaleS; - float _scaleT; -}; - -/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, -/// creating a new entry in the list if necessary. -uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); - -/// Utility method for editing: removes any unused materials from the supplied list. -void clearUnusedMaterials(QVector& materials, const QByteArray& contents); - -typedef QExplicitlySharedDataPointer VoxelColorDataPointer; - -/// Contains a block of voxel color data. -class VoxelColorData : public DataBlock { -public: - - VoxelColorData(const QVector& contents, int size); - VoxelColorData(Bitstream& in, int bytes); - VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference); - - const QVector& getContents() const { return _contents; } - - int getSize() const { return _size; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QVector _contents; - int _size; -}; - -/// An attribute that stores voxel colors. -class VoxelColorAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE VoxelColorAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; -}; - -typedef QExplicitlySharedDataPointer VoxelMaterialDataPointer; - -/// Contains a block of voxel material data. -class VoxelMaterialData : public DataBlock { -public: - - VoxelMaterialData(const QByteArray& contents, int size, - const QVector& materials = QVector()); - VoxelMaterialData(Bitstream& in, int bytes); - VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference); - - const QByteArray& getContents() const { return _contents; } - - int getSize() const { return _size; } - - const QVector& getMaterials() const { return _materials; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; - int _size; - QVector _materials; -}; - -/// An attribute that stores voxel materials. -class VoxelMaterialAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE VoxelMaterialAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; -}; - -typedef QExplicitlySharedDataPointer VoxelHermiteDataPointer; - -/// Contains a block of voxel Hermite data (positions and normals at edge crossings). -class VoxelHermiteData : public DataBlock { -public: - - static const int EDGE_COUNT = 3; - - VoxelHermiteData(const QVector& contents, int size); - VoxelHermiteData(Bitstream& in, int bytes); - VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference); - - const QVector& getContents() const { return _contents; } - - int getSize() const { return _size; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QVector _contents; - int _size; -}; - -/// An attribute that stores voxel Hermite data. -class VoxelHermiteAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; -}; - /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index ab0fe34552..681d16d589 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -194,304 +194,12 @@ void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedOb } } -const int VOXEL_BLOCK_SIZE = 16; -const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1; -const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES; -const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES; - HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& averageColor) : MaterialEdit(material, averageColor), spanner(spanner) { } -class HeightfieldMaterialSpannerEditVisitor : public MetavoxelVisitor { -public: - - HeightfieldMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color); - - virtual int visit(MetavoxelInfo& info); - -private: - - Spanner* _spanner; - SharedObjectPointer _material; - QColor _color; - float _blockSize; -}; - -HeightfieldMaterialSpannerEditVisitor::HeightfieldMaterialSpannerEditVisitor(Spanner* spanner, - const SharedObjectPointer& material, const QColor& color) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << - AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), - _spanner(spanner), - _material(material), - _color(color), - _blockSize(spanner->getVoxelizationGranularity() * VOXEL_BLOCK_SIZE) { -} - -int HeightfieldMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - if (!bounds.intersects(_spanner->getBounds())) { - return STOP_RECURSION; - } - if (info.size > _blockSize) { - return DEFAULT_ORDER; - } - QVector oldColorContents; - VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - if (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) { - oldColorContents = colorPointer->getContents(); - } else { - oldColorContents = QVector(VOXEL_BLOCK_VOLUME); - } - - QVector hermiteContents; - VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue(); - if (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) { - hermiteContents = hermitePointer->getContents(); - } else { - hermiteContents = QVector(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT); - } - - QByteArray materialContents; - QVector materials; - VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); - if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) { - materialContents = materialPointer->getContents(); - materials = materialPointer->getMaterials(); - } else { - materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0); - } - - float scale = VOXEL_BLOCK_SIZE / info.size; - QVector colorContents = oldColorContents; - - Box overlap = info.getBounds().getIntersection(_spanner->getBounds()); - overlap.minimum = (overlap.minimum - info.minimum) * scale; - overlap.maximum = (overlap.maximum - info.minimum) * scale; - int minX = glm::ceil(overlap.minimum.x); - int minY = glm::ceil(overlap.minimum.y); - int minZ = glm::ceil(overlap.minimum.z); - int sizeX = (int)overlap.maximum.x - minX + 1; - int sizeY = (int)overlap.maximum.y - minY + 1; - int sizeZ = (int)overlap.maximum.z - minZ + 1; - - bool flipped = false; - float step = 1.0f / scale; - glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step); - if (_spanner->hasOwnColors()) { - for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - *destX = _spanner->getColorAt(position); - } - } - } - } - } else { - QRgb rgb = _color.rgba(); - flipped = (qAlpha(rgb) == 0); - for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - *destX = rgb; - } - } - } - } - } - - // if there are no visible colors, we can clear everything - bool foundOpaque = false; - for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) { - if (qAlpha(*src) != 0) { - foundOpaque = true; - break; - } - } - if (!foundOpaque) { - info.outputValues[0] = AttributeValue(_outputs.at(0)); - info.outputValues[1] = AttributeValue(_outputs.at(1)); - info.outputValues[2] = AttributeValue(_outputs.at(2)); - return STOP_RECURSION; - } - - VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES)); - info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), - encodeInline(newColorPointer)); - - int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT; - int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT; - int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ; - int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ; - if (minX > 0) { - hermiteMinX--; - hermiteSizeX++; - } - if (minY > 0) { - hermiteMinY--; - hermiteSizeY++; - } - 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) { - QRgb* hermiteDestY = hermiteDestZ; - for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) { - QRgb* hermiteDestX = hermiteDestY; - for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, - hermiteDestX += VoxelHermiteData::EDGE_COUNT) { - // at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the - // crossing and normal values based on intersection with the sphere - float distance; - glm::vec3 normal; - if (x != VOXEL_BLOCK_SIZE) { - int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - const QRgb* color = colorContents.constData() + offset; - int alpha0 = qAlpha(color[0]); - int alpha1 = qAlpha(color[1]); - if (alpha0 != alpha1) { - if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step, - info.minimum + glm::vec3(x + 1, y, z) * step, distance, normal)) { - const QRgb* oldColor = oldColorContents.constData() + offset; - if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) { - int alpha = distance * EIGHT_BIT_MAXIMUM; - if (normal.x < 0.0f ? alpha <= qAlpha(hermiteDestX[0]) : alpha >= qAlpha(hermiteDestX[0])) { - hermiteDestX[0] = packNormal(flipped ? -normal : normal, alpha); - } - } else { - hermiteDestX[0] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM); - } - } - } else { - hermiteDestX[0] = 0x0; - } - } else { - hermiteDestX[0] = 0x0; - } - if (y != VOXEL_BLOCK_SIZE) { - int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - const QRgb* color = colorContents.constData() + offset; - int alpha0 = qAlpha(color[0]); - int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]); - if (alpha0 != alpha2) { - if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step, - info.minimum + glm::vec3(x, y + 1, z) * step, distance, normal)) { - const QRgb* oldColor = oldColorContents.constData() + offset; - if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) { - int alpha = distance * EIGHT_BIT_MAXIMUM; - if (normal.y < 0.0f ? alpha <= qAlpha(hermiteDestX[1]) : alpha >= qAlpha(hermiteDestX[1])) { - hermiteDestX[1] = packNormal(flipped ? -normal : normal, alpha); - } - } else { - hermiteDestX[1] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM); - } - } - } else { - hermiteDestX[1] = 0x0; - } - } else { - hermiteDestX[1] = 0x0; - } - if (z != VOXEL_BLOCK_SIZE) { - int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - const QRgb* color = colorContents.constData() + offset; - int alpha0 = qAlpha(color[0]); - int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]); - if (alpha0 != alpha4) { - if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step, - info.minimum + glm::vec3(x, y, z + 1) * step, distance, normal)) { - const QRgb* oldColor = oldColorContents.constData() + offset; - if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) { - int alpha = distance * EIGHT_BIT_MAXIMUM; - if (normal.z < 0.0f ? alpha <= qAlpha(hermiteDestX[2]) : alpha >= qAlpha(hermiteDestX[2])) { - hermiteDestX[2] = packNormal(flipped ? -normal : normal, alpha); - } - } else { - hermiteDestX[2] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM); - } - } - } else { - hermiteDestX[2] = 0x0; - } - } else { - hermiteDestX[2] = 0x0; - } - } - } - } - VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES)); - info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), - encodeInline(newHermitePointer)); - - if (_spanner->hasOwnMaterials()) { - QHash materialMap; - position.z = info.minimum.z + minZ * step; - for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - int material = _spanner->getMaterialAt(position); - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(_spanner->getMaterials().at(material - 1), materials, - materialContents); - } - material = mapping; - } - *destX = material; - } - } - } - } - } else { - uchar materialIndex = getMaterialIndex(_material, materials, materialContents); - position.z = info.minimum.z + minZ * step; - for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - *destX = materialIndex; - } - } - } - } - } - clearUnusedMaterials(materials, materialContents); - VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials)); - info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline(newMaterialPointer)); - - return STOP_RECURSION; -} - void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // make sure the color is either 100% transparent or 100% opaque QColor color = averageColor; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 77464d6226..4ea51a44ce 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -34,6 +34,7 @@ REGISTER_META_OBJECT(Heightfield) REGISTER_META_OBJECT(Sphere) REGISTER_META_OBJECT(Cuboid) REGISTER_META_OBJECT(StaticModel) +REGISTER_META_OBJECT(MaterialObject) static int heightfieldHeightTypeId = registerSimpleMetaType(); static int heightfieldColorTypeId = registerSimpleMetaType(); @@ -362,6 +363,9 @@ QByteArray StaticModel::getRendererClassName() const { return "StaticModelRenderer"; } +DataBlock::~DataBlock() { +} + const int HeightfieldData::SHARED_EDGE = 1; HeightfieldData::HeightfieldData(int width) : @@ -1115,6 +1119,74 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } } +MaterialObject::MaterialObject() : + _scaleS(1.0f), + _scaleT(1.0f) { +} + +static QHash countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + +const float EIGHT_BIT_MAXIMUM = 255.0f; + +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { + if (!(material && static_cast(material.data())->getDiffuse().isValid())) { + return 0; + } + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& existingMaterial = materials.at(i); + if (existingMaterial) { + if (existingMaterial->equals(material.data())) { + return i + 1; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = material; + return firstEmptyIndex + 1; + } + if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(material); + return materials.size(); + } + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + uchar materialIndex = 0; + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)materialIndex, (char)0); + return materialIndex; +} + +void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } + } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } +} + static QByteArray encodeHeightfieldStack(int offsetX, int offsetY, int width, int height, const QVector& contents) { QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); @@ -2273,44 +2345,12 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; - if (stackDest->isEmpty() && *heightLineDest != 0 && false) { - // initialize from heightfield - *stackDest = StackArray(1); - float voxelHeight = *heightLineDest * voxelScale; - stackDest->setPosition(glm::floor(voxelHeight)); - StackArray::Entry* entryDest = stackDest->getEntryData(); - - if (colorDest) { - entryDest->color = qRgb(colorDest[0], colorDest[1], colorDest[2]); - } - - if (materialDest) { - int index = *materialDest; - if (index != 0) { - int& mapping = materialMappings[index]; - if (mapping == 0) { - mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), - newStackMaterials, dummyContents); - } - index = mapping; - } - entryDest->material = index; - } - - quint16 left = heightLineDest[-1]; - quint16 right = heightLineDest[1]; - quint16 up = heightLineDest[heightWidth]; - quint16 down = heightLineDest[-heightWidth]; - - entryDest->setHermiteY(glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : - (left - right), 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : - (down - up))), glm::fract(voxelHeight)); - } + int prepend = 0, append = 0; if (!stackDest->isEmpty()) { int oldBottom = stackDest->getPosition(); int oldTop = oldBottom + stackDest->getEntryCount() - 1; - int prepend = qMax(0, oldBottom - newBottom); - int append = qMax(0, newTop - oldTop); + prepend = qMax(0, oldBottom - newBottom); + append = qMax(0, newTop - oldTop); if (prepend != 0 || append != 0) { StackArray newStack(prepend + stackDest->getEntryCount() + append); memcpy(newStack.getEntryData() + prepend, stackDest->getEntryData(), @@ -2326,6 +2366,46 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } else { *stackDest = StackArray(newTop - newBottom + 1); stackDest->setPosition(newBottom); + prepend = stackDest->getEntryCount(); + } + if (*heightLineDest != 0) { + float voxelHeight = *heightLineDest * voxelScale; + for (int i = 0, total = prepend + append; i < total; i++) { + int offset = (i < prepend) ? i : stackDest->getEntryCount() - append + (i - prepend); + int y = stackDest->getPosition() + offset; + if (y <= voxelHeight) { + StackArray::Entry* entryDest = stackDest->getEntryData() + offset; + if (colorDest) { + entryDest->color = qRgb(colorDest[0], colorDest[1], colorDest[2]); + } + if (materialDest) { + int index = *materialDest; + if (index != 0) { + int& mapping = materialMappings[index]; + if (mapping == 0) { + mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), + newStackMaterials, dummyContents); + } + index = mapping; + } + entryDest->material = index; + } + if (y + 1 > voxelHeight) { + *heightLineDest = 0; + + const quint16* oldHeightLineDest = _height->getContents().constData() + + (int)z * heightWidth + (int)x; + quint16 left = oldHeightLineDest[-1]; + quint16 right = oldHeightLineDest[1]; + quint16 up = oldHeightLineDest[heightWidth]; + quint16 down = oldHeightLineDest[-heightWidth]; + + entryDest->setHermiteY(glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : + (left - right), 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : + (down - up))), voxelHeight - y); + } + } + } } StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); glm::vec3 pos = worldPos; diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index fa51a2f0d8..ec3cb04f52 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -18,6 +18,7 @@ #include "MetavoxelUtil.h" class AbstractHeightfieldNodeRenderer; +class DataBlock; class Heightfield; class HeightfieldColor; class HeightfieldHeight; @@ -293,6 +294,42 @@ private: QUrl _url; }; +typedef QExplicitlySharedDataPointer DataBlockPointer; + +/// Base class for blocks of data. +class DataBlock : public QSharedData { +public: + + static const int COLOR_BYTES = 3; + + virtual ~DataBlock(); + + void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; } + const DataBlockPointer& getDeltaData() const { return _deltaData; } + + void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; } + const QByteArray& getEncodedDelta() const { return _encodedDelta; } + + QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; } + +protected: + + QByteArray _encoded; + QMutex _encodedMutex; + + DataBlockPointer _deltaData; + QByteArray _encodedDelta; + QMutex _encodedDeltaMutex; + + class EncodedSubdivision { + public: + DataBlockPointer ancestor; + QByteArray data; + }; + QVector _encodedSubdivisions; + QMutex _encodedSubdivisionsMutex; +}; + /// Base class for heightfield data blocks. class HeightfieldData : public DataBlock { public: @@ -467,6 +504,36 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value); template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference); +/// Contains the description of a material. +class MaterialObject : public SharedObject { + Q_OBJECT + Q_PROPERTY(QUrl diffuse MEMBER _diffuse) + Q_PROPERTY(float scaleS MEMBER _scaleS) + Q_PROPERTY(float scaleT MEMBER _scaleT) + +public: + + Q_INVOKABLE MaterialObject(); + + const QUrl& getDiffuse() const { return _diffuse; } + + float getScaleS() const { return _scaleS; } + float getScaleT() const { return _scaleT; } + +private: + + QUrl _diffuse; + float _scaleS; + float _scaleT; +}; + +/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, +/// creating a new entry in the list if necessary. +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); + +/// Utility method for editing: removes any unused materials from the supplied list. +void clearUnusedMaterials(QVector& materials, const QByteArray& contents); + typedef QExplicitlySharedDataPointer HeightfieldStackPointer; /// A single column within a stack block. From 5a46f3e20d12df81987c3b29e9f7fcd0f0cdc96f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 5 Jan 2015 18:37:47 -0800 Subject: [PATCH 30/67] Material/ray testing bits. --- interface/src/MetavoxelSystem.cpp | 28 +- interface/src/MetavoxelSystem.h | 7 +- libraries/metavoxels/src/Spanner.cpp | 425 ++++++++++++++++---------- libraries/metavoxels/src/Spanner.h | 16 +- libraries/shared/src/GeometryUtil.cpp | 4 +- 5 files changed, 302 insertions(+), 178 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d1336baf9e..75d224221f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1044,16 +1044,17 @@ VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector _materials(materials) { } -bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, - const glm::vec3& direction, float& distance) const { +bool VoxelBuffer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float boundsDistance, float& distance) const { float highest = _size - 1.0f; - glm::vec3 position = entry * highest; + glm::vec3 position = (origin + direction * boundsDistance) * highest; glm::vec3 floors = glm::floor(position); int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); forever { - for (QMultiHash::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); - it != _quadIndices.constEnd(); it++) { + VoxelCoord key(qRgb(x, y, z)); + for (QMultiHash::const_iterator it = _quadIndices.constFind(key); + it != _quadIndices.constEnd() && it.key() == key; it++) { const int* indices = _indices.constData() + *it; if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, _vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) || @@ -1397,6 +1398,19 @@ HeightfieldNodeRenderer::~HeightfieldNodeRenderer() { Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); } +bool HeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, + const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, + float boundsDistance, float& distance) const { + if (!_voxels) { + return false; + } + glm::quat inverseRotation = glm::inverse(rotation); + float inverseScale = 1.0f / scale.x; + return static_cast(_voxels.data())->findRayIntersection( + inverseRotation * (origin - translation) * inverseScale, inverseRotation * direction * inverseScale, + boundsDistance, distance); +} + class EdgeCrossing { public: glm::vec3 point; @@ -2030,8 +2044,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } indicesZ.swap(lastIndicesZ); } - - _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, width, node->getStack()->getMaterials()); + _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth, + node->getStack()->getMaterials()); } if (_voxels) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 45728901a5..e17edeafdc 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -323,9 +323,7 @@ public: bool isHermiteEnabled() const { return _hermiteEnabled; } /// Finds the first intersection between the described ray and the voxel data. - /// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1) - bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, - const glm::vec3& direction, float& distance) const; + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; virtual void render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor = false); @@ -425,6 +423,9 @@ public: HeightfieldNodeRenderer(); virtual ~HeightfieldNodeRenderer(); + virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; + void render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 4ea51a44ce..7720b0b739 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1134,9 +1134,8 @@ static QHash countIndices(const QByteArray& contents) { return counts; } -const float EIGHT_BIT_MAXIMUM = 255.0f; - -uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { +static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, + QByteArray& contents) { if (!(material && static_cast(material.data())->getDiffuse().isValid())) { return 0; } @@ -1157,7 +1156,7 @@ uchar getMaterialIndex(const SharedObjectPointer& material, QVector::max()) { materials.append(material); return materials.size(); } @@ -1175,7 +1174,85 @@ uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, const QByteArray& contents) { +static void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } + } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } +} + +static QHash countIndices(const QVector& contents) { + QHash counts; + foreach (const StackArray& array, contents) { + if (array.isEmpty()) { + continue; + } + for (const StackArray::Entry* entry = array.getEntryData(), *end = entry + array.getEntryCount(); + entry != end; entry++) { + if (entry->material != 0) { + counts[entry->material]++; + } + } + } + return counts; +} + +static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, + QVector& contents) { + if (!(material && static_cast(material.data())->getDiffuse().isValid())) { + return 0; + } + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& existingMaterial = materials.at(i); + if (existingMaterial) { + if (existingMaterial->equals(material.data())) { + return i + 1; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = material; + return firstEmptyIndex + 1; + } + if (materials.size() < numeric_limits::max()) { + materials.append(material); + return materials.size(); + } + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + uchar materialIndex = 0; + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + for (StackArray* array = contents.data(), *end = array + contents.size(); array != end; array++) { + if (array->isEmpty()) { + continue; + } + for (StackArray::Entry* entry = array->getEntryData(), *end = entry + array->getEntryCount(); + entry != end; entry++) { + if (entry->material == materialIndex) { + entry->material = 0; + } + } + } + return materialIndex; +} + +static void clearUnusedMaterials(QVector& materials, const QVector& contents) { QHash counts = countIndices(contents); for (int i = 0; i < materials.size(); i++) { if (counts.value(i + 1) == 0) { @@ -1688,18 +1765,26 @@ float HeightfieldNode::getHeight(const glm::vec3& location) const { return interpolatedHeight / numeric_limits::max(); } -bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool HeightfieldNode::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 inverseScale = 1.0f / scale; + glm::vec3 transformedOrigin = inverseRotation * (origin - translation) * inverseScale; + glm::vec3 transformedDirection = inverseRotation * direction * inverseScale; float boundsDistance; - if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(origin, direction, boundsDistance)) { + if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(transformedOrigin, transformedDirection, + boundsDistance)) { return false; } if (!isLeaf()) { float closestDistance = FLT_MAX; for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); float childDistance; - if (_children[i]->findRayIntersection(origin * glm::vec3(2.0f, 1.0f, 2.0f) - - glm::vec3(i & X_MAXIMUM_FLAG ? 1.0f : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? 1.0f : 0.0f), - direction * glm::vec3(2.0f, 1.0f, 2.0f), childDistance)) { + if (_children[i]->findRayIntersection(translation + + rotation * glm::vec3(i & X_MAXIMUM_FLAG ? nextScale.x : 0.0f, 0.0f, + i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, + nextScale, origin, direction, childDistance)) { closestDistance = qMin(closestDistance, childDistance); } } @@ -1709,148 +1794,21 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::ve distance = closestDistance; return true; } - if (!_height) { + float shortestDistance = FLT_MAX; + float heightfieldDistance; + if (findHeightfieldRayIntersection(transformedOrigin, transformedDirection, boundsDistance, heightfieldDistance)) { + shortestDistance = heightfieldDistance; + } + float rendererDistance; + if (_renderer && _renderer->findRayIntersection(translation, rotation, scale, origin, direction, boundsDistance, + rendererDistance)) { + shortestDistance = qMin(shortestDistance, rendererDistance); + } + if (shortestDistance == FLT_MAX) { return false; } - int width = _height->getWidth(); - const QVector& contents = _height->getContents(); - const quint16* src = contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; - int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; - - glm::vec3 scale((float)innerWidth, (float)numeric_limits::max(), (float)innerHeight); - glm::vec3 dir = direction * scale; - glm::vec3 entry = origin * scale + dir * boundsDistance; - - entry.x += HeightfieldHeight::HEIGHT_BORDER; - entry.z += HeightfieldHeight::HEIGHT_BORDER; - glm::vec3 floors = glm::floor(entry); - glm::vec3 ceils = glm::ceil(entry); - if (floors.x == ceils.x) { - if (dir.x > 0.0f) { - ceils.x += 1.0f; - } else { - floors.x -= 1.0f; - } - } - if (floors.z == ceils.z) { - if (dir.z > 0.0f) { - ceils.z += 1.0f; - } else { - floors.z -= 1.0f; - } - } - - bool withinBounds = true; - float accumulatedDistance = 0.0f; - while (withinBounds) { - // find the heights at the corners of the current cell - int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); - int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); - float upperLeft = src[floorZ * width + floorX]; - float upperRight = src[floorZ * width + ceilX]; - float lowerLeft = src[ceilZ * width + floorX]; - float lowerRight = src[ceilZ * width + ceilX]; - - // find the distance to the next x coordinate - float xDistance = FLT_MAX; - if (dir.x > 0.0f) { - xDistance = (ceils.x - entry.x) / dir.x; - } else if (dir.x < 0.0f) { - xDistance = (floors.x - entry.x) / dir.x; - } - - // and the distance to the next z coordinate - float zDistance = FLT_MAX; - if (dir.z > 0.0f) { - zDistance = (ceils.z - entry.z) / dir.z; - } else if (dir.z < 0.0f) { - zDistance = (floors.z - entry.z) / dir.z; - } - - // the exit distance is the lower of those two - float exitDistance = qMin(xDistance, zDistance); - glm::vec3 exit, nextFloors = floors, nextCeils = ceils; - if (exitDistance == FLT_MAX) { - if (dir.y > 0.0f) { - return false; // line points upwards; no collisions possible - } - withinBounds = false; // line points downwards; check this cell only - - } else { - // find the exit point and the next cell, and determine whether it's still within the bounds - exit = entry + exitDistance * dir; - withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); - if (exitDistance == xDistance) { - if (dir.x > 0.0f) { - nextFloors.x += 1.0f; - withinBounds &= (nextCeils.x += 1.0f) <= highestX; - } else { - withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.x -= 1.0f; - } - } - if (exitDistance == zDistance) { - if (dir.z > 0.0f) { - nextFloors.z += 1.0f; - withinBounds &= (nextCeils.z += 1.0f) <= highestZ; - } else { - withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; - nextCeils.z -= 1.0f; - } - } - // check the vertical range of the ray against the ranges of the cell heights - if (qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || - qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - continue; - } - } - // having passed the bounds check, we must check against the planes - glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); - - // first check the triangle including the Z+ segment - glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); - float lowerProduct = glm::dot(lowerNormal, dir); - if (lowerProduct < 0.0f) { - float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.z >= intersection.x) { - distance = boundsDistance + accumulatedDistance + planeDistance; - return true; - } - } - - // then the one with the X+ segment - glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); - float upperProduct = glm::dot(upperNormal, dir); - if (upperProduct < 0.0f) { - float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; - glm::vec3 intersection = relativeEntry + planeDistance * dir; - if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && - intersection.x >= intersection.z) { - distance = boundsDistance + accumulatedDistance + planeDistance; - return true; - } - } - - // no joy; continue on our way - entry = exit; - floors = nextFloors; - ceils = nextCeils; - accumulatedDistance += exitDistance; - } - - return false; + distance = shortestDistance; + return true; } HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const glm::vec3& radius, @@ -2316,8 +2274,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); QRgb rgba = color.rgba(); bool erase = (color.alpha() == 0); - QByteArray dummyContents; - uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, dummyContents); + uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, newStackContents); bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); QHash materialMappings; @@ -2384,7 +2341,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int& mapping = materialMappings[index]; if (mapping == 0) { mapping = getMaterialIndex(newMaterialMaterials.at(index - 1), - newStackMaterials, dummyContents); + newStackMaterials, newStackContents); } index = mapping; } @@ -2424,7 +2381,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons int& mapping = materialMappings[index]; if (mapping == 0) { mapping = getMaterialIndex(spanner->getMaterials().at(index - 1), - newStackMaterials, dummyContents); + newStackMaterials, newStackContents); } index = mapping; } @@ -2520,6 +2477,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } } clearUnusedMaterials(newMaterialMaterials, newMaterialContents); + clearUnusedMaterials(newStackMaterials, newStackContents); return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), @@ -3011,9 +2969,162 @@ void HeightfieldNode::maybeRenormalize(const glm::vec3& scale, float normalizeSc } } +bool HeightfieldNode::findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float boundsDistance, float& distance) const { + if (!_height) { + return false; + } + int width = _height->getWidth(); + const QVector& contents = _height->getContents(); + const quint16* src = contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = innerWidth + HeightfieldHeight::HEIGHT_BORDER; + int highestZ = innerHeight + HeightfieldHeight::HEIGHT_BORDER; + + glm::vec3 heightScale((float)innerWidth, (float)numeric_limits::max(), (float)innerHeight); + glm::vec3 dir = direction * heightScale; + glm::vec3 entry = origin * heightScale + dir * boundsDistance; + + entry.x += HeightfieldHeight::HEIGHT_BORDER; + entry.z += HeightfieldHeight::HEIGHT_BORDER; + glm::vec3 floors = glm::floor(entry); + glm::vec3 ceils = glm::ceil(entry); + if (floors.x == ceils.x) { + if (dir.x > 0.0f) { + ceils.x += 1.0f; + } else { + floors.x -= 1.0f; + } + } + if (floors.z == ceils.z) { + if (dir.z > 0.0f) { + ceils.z += 1.0f; + } else { + floors.z -= 1.0f; + } + } + + bool withinBounds = true; + float accumulatedDistance = 0.0f; + while (withinBounds) { + // find the heights at the corners of the current cell + int floorX = qMin(qMax((int)floors.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int floorZ = qMin(qMax((int)floors.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + int ceilX = qMin(qMax((int)ceils.x, HeightfieldHeight::HEIGHT_BORDER), highestX); + int ceilZ = qMin(qMax((int)ceils.z, HeightfieldHeight::HEIGHT_BORDER), highestZ); + float upperLeft = src[floorZ * width + floorX]; + float upperRight = src[floorZ * width + ceilX]; + float lowerLeft = src[ceilZ * width + floorX]; + float lowerRight = src[ceilZ * width + ceilX]; + + // find the distance to the next x coordinate + float xDistance = FLT_MAX; + if (dir.x > 0.0f) { + xDistance = (ceils.x - entry.x) / dir.x; + } else if (dir.x < 0.0f) { + xDistance = (floors.x - entry.x) / dir.x; + } + + // and the distance to the next z coordinate + float zDistance = FLT_MAX; + if (dir.z > 0.0f) { + zDistance = (ceils.z - entry.z) / dir.z; + } else if (dir.z < 0.0f) { + zDistance = (floors.z - entry.z) / dir.z; + } + + // the exit distance is the lower of those two + float exitDistance = qMin(xDistance, zDistance); + glm::vec3 exit, nextFloors = floors, nextCeils = ceils; + if (exitDistance == FLT_MAX) { + if (dir.y > 0.0f) { + return false; // line points upwards; no collisions possible + } + withinBounds = false; // line points downwards; check this cell only + + } else { + // find the exit point and the next cell, and determine whether it's still within the bounds + exit = entry + exitDistance * dir; + withinBounds = (exit.y >= 0.0f && exit.y <= numeric_limits::max()); + if (exitDistance == xDistance) { + if (dir.x > 0.0f) { + nextFloors.x += 1.0f; + withinBounds &= (nextCeils.x += 1.0f) <= highestX; + } else { + withinBounds &= (nextFloors.x -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.x -= 1.0f; + } + } + if (exitDistance == zDistance) { + if (dir.z > 0.0f) { + nextFloors.z += 1.0f; + withinBounds &= (nextCeils.z += 1.0f) <= highestZ; + } else { + withinBounds &= (nextFloors.z -= 1.0f) >= HeightfieldHeight::HEIGHT_BORDER; + nextCeils.z -= 1.0f; + } + } + // check the vertical range of the ray against the ranges of the cell heights + if (upperLeft == 0 || upperRight == 0 || lowerLeft == 0 || lowerRight == 0 || + qMin(entry.y, exit.y) > qMax(qMax(upperLeft, upperRight), qMax(lowerLeft, lowerRight)) || + qMax(entry.y, exit.y) < qMin(qMin(upperLeft, upperRight), qMin(lowerLeft, lowerRight))) { + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + continue; + } + } + // having passed the bounds check, we must check against the planes + glm::vec3 relativeEntry = entry - glm::vec3(floors.x, upperLeft, floors.z); + + // first check the triangle including the Z+ segment + glm::vec3 lowerNormal(lowerLeft - lowerRight, 1.0f, upperLeft - lowerLeft); + float lowerProduct = glm::dot(lowerNormal, dir); + if (lowerProduct < 0.0f) { + float planeDistance = -glm::dot(lowerNormal, relativeEntry) / lowerProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.z >= intersection.x) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // then the one with the X+ segment + glm::vec3 upperNormal(upperLeft - upperRight, 1.0f, upperRight - lowerRight); + float upperProduct = glm::dot(upperNormal, dir); + if (upperProduct < 0.0f) { + float planeDistance = -glm::dot(upperNormal, relativeEntry) / upperProduct; + glm::vec3 intersection = relativeEntry + planeDistance * dir; + if (intersection.x >= 0.0f && intersection.x <= 1.0f && intersection.z >= 0.0f && intersection.z <= 1.0f && + intersection.x >= intersection.z) { + distance = boundsDistance + accumulatedDistance + planeDistance; + return true; + } + } + + // no joy; continue on our way + entry = exit; + floors = nextFloors; + ceils = nextCeils; + accumulatedDistance += exitDistance; + } + + return false; +} + AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() { } +bool AbstractHeightfieldNodeRenderer::findRayIntersection(const glm::vec3& translation, + const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, + float boundsDistance, float& distance) const { + return false; +} + Heightfield::Heightfield() : _aspectY(1.0f), _aspectZ(1.0f) { @@ -3097,10 +3208,8 @@ float Heightfield::getHeight(const glm::vec3& location) const { } bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - glm::quat inverseRotation = glm::inverse(getRotation()); - glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); - return _root->findRayIntersection(inverseRotation * (origin - getTranslation()) * inverseScale, - inverseRotation * direction * inverseScale, distance); + return _root->findRayIntersection(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + getScale() * _aspectZ), origin, direction, distance); } Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index ec3cb04f52..37af85b584 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -527,13 +527,6 @@ private: float _scaleT; }; -/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, -/// creating a new entry in the list if necessary. -uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); - -/// Utility method for editing: removes any unused materials from the supplied list. -void clearUnusedMaterials(QVector& materials, const QByteArray& contents); - typedef QExplicitlySharedDataPointer HeightfieldStackPointer; /// A single column within a stack block. @@ -690,7 +683,8 @@ public: float getHeight(const glm::vec3& location) const; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& origin, const glm::vec3& direction, float& distance) const; HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material, const QColor& color); @@ -733,6 +727,9 @@ private: void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth, QVector& heightContents, QVector& stackContents); + bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float boundsDistance, float& distance) const; + HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; @@ -748,6 +745,9 @@ class AbstractHeightfieldNodeRenderer { public: virtual ~AbstractHeightfieldNodeRenderer(); + + virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const; }; /// A heightfield represented as a spanner. diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 5376883438..aaec8b3e4a 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -262,7 +262,7 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc return false; // origin below plane } float divisor = glm::dot(normal, direction); - if (divisor > -EPSILON) { + if (divisor >= 0.0f) { return false; } float t = dividend / divisor; @@ -490,4 +490,4 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len vertexArrayA[i] = vertexArrayB[i]; } } -} \ No newline at end of file +} From a20b12ad44f5e0c420d2f91d276500907eaab27a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 6 Jan 2015 14:25:26 -0800 Subject: [PATCH 31/67] Material painting for stacks. --- interface/src/MetavoxelSystem.cpp | 23 +- interface/src/MetavoxelSystem.h | 5 +- interface/src/ui/MetavoxelEditor.cpp | 12 +- .../metavoxels/src/MetavoxelMessages.cpp | 36 +-- libraries/metavoxels/src/MetavoxelMessages.h | 21 +- libraries/metavoxels/src/Spanner.cpp | 221 ++++-------------- libraries/metavoxels/src/Spanner.h | 19 +- 7 files changed, 93 insertions(+), 244 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 75d224221f..f7ee827524 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -528,23 +528,28 @@ void MetavoxelSystem::refreshVoxelData() { } void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, - SharedObjectPointer(), color)) }; - applyEdit(edit, true); + Sphere* sphere = new Sphere(); + sphere->setTranslation(position); + sphere->setScale(radius); + setHeightfieldColor(SharedObjectPointer(sphere), color, true); } void MetavoxelSystem::paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material) { - MetavoxelEditMessage edit = { QVariant::fromValue(PaintHeightfieldMaterialEdit(position, radius, material)) }; - applyMaterialEdit(edit, true); + Sphere* sphere = new Sphere(); + sphere->setTranslation(position); + sphere->setScale(radius); + setHeightfieldMaterial(SharedObjectPointer(sphere), material, true); } -void MetavoxelSystem::setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color) { - MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, SharedObjectPointer(), color)) }; +void MetavoxelSystem::setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint) { + MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, + SharedObjectPointer(), color, paint)) }; applyEdit(edit, true); } -void MetavoxelSystem::setHeightfieldMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material) { - MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, material)) }; +void MetavoxelSystem::setHeightfieldMaterial(const SharedObjectPointer& spanner, + const SharedObjectPointer& material, bool paint) { + MetavoxelEditMessage edit = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner, material, QColor(), paint)) }; applyMaterialEdit(edit, true); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index e17edeafdc..98c7ff9965 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -68,9 +68,10 @@ public: Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material); - Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color); + Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint = false); - Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material); + Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner, + const SharedObjectPointer& material, bool paint = false); void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); } void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 971b99c89a..89abb97fb9 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -937,12 +937,16 @@ HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* edit } QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) { + Sphere* sphere = new Sphere(); + sphere->setTranslation(_position); + sphere->setScale(_radius->value()); if (alternate) { - return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); + return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), + SharedObjectPointer(), QColor(0, 0, 0, 0), true)); } else { - return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), _materialControl->getMaterial(), - _materialControl->getColor())); - } + return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), + _materialControl->getMaterial(), _materialControl->getColor(), true)); + } } HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) : diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 681d16d589..50808156a7 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -173,44 +173,28 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av averageColor(averageColor) { } -PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& averageColor) : - MaterialEdit(material, averageColor), - position(position), - radius(radius) { -} - -void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - 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())->paintMaterial(position, radius, material, averageColor); - if (newSpanner != spanner) { - data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner); - } - } -} - HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, const QColor& averageColor) : + const SharedObjectPointer& material, const QColor& averageColor, bool paint) : MaterialEdit(material, averageColor), - spanner(spanner) { + spanner(spanner), + paint(paint) { } void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - // make sure the color is either 100% transparent or 100% opaque + // make sure the color meets our transparency requirements QColor color = averageColor; - color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f); + if (paint) { + color.setAlphaF(1.0f); + } else if (color.alphaF() < 0.5f) { + color = QColor(0, 0, 0, 0); + } QVector results; data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), static_cast(spanner.data())->getBounds(), results); foreach (const SharedObjectPointer& result, results) { - Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color); + Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color, paint); if (newResult != result) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 00c4353603..4b941eaad2 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -225,23 +225,6 @@ public: DECLARE_STREAMABLE_METATYPE(MaterialEdit) -/// An edit that sets a region of a heightfield material. -class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit { - STREAMABLE - -public: - - STREAM glm::vec3 position; - STREAM float radius; - - PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, - const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); - - virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; -}; - -DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit) - /// An edit that sets the materials of a heightfield within a spanner to a value. class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit { STREAMABLE @@ -249,9 +232,11 @@ class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit { public: STREAM SharedObjectPointer spanner; + STREAM bool paint; HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), - const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); + const SharedObjectPointer& material = SharedObjectPointer(), + const QColor& averageColor = QColor(), bool paint = false); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 7720b0b739..15455bf251 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -111,17 +111,12 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } -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; } Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color) { + const QColor& color, bool paint) { return this; } @@ -1811,139 +1806,6 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& translation, const gl return true; } -HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const glm::vec3& radius, - const SharedObjectPointer& material, const QColor& color) { - if (position.x + radius.x < 0.0f || position.z + radius.z < 0.0f || - position.x - radius.x > 1.0f || position.z - radius.z > 1.0f) { - return this; - } - if (!isLeaf()) { - HeightfieldNode* newNode = this; - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldNode* newChild = _children[i]->paintMaterial(position * glm::vec3(2.0f, 1.0f, 2.0f) - - glm::vec3(i & X_MAXIMUM_FLAG ? 1.0f : 0.0f, 0.0f, i & Y_MAXIMUM_FLAG ? 1.0f : 0.0f), - radius * glm::vec3(2.0f, 1.0f, 2.0f), material, color); - if (_children[i] != newChild) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - newNode->setChild(i, HeightfieldNodePointer(newChild)); - } - } - if (newNode != this) { - newNode->mergeChildren(false, true); - } - return newNode; - } - if (!_height) { - return this; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int baseWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; - int baseHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE; - - int colorWidth = baseWidth, colorHeight = baseHeight; - QByteArray colorContents; - if (_color) { - colorWidth = _color->getWidth(); - colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - colorContents = _color->getContents(); - - } else { - colorContents = QByteArray(baseWidth * baseHeight * DataBlock::COLOR_BYTES, 0xFF); - } - - int materialWidth = colorWidth, materialHeight = colorHeight; - QByteArray materialContents; - QVector materials; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - materialContents = _material->getContents(); - materials = _material->getMaterials(); - - } else { - materialContents = QByteArray(materialWidth * materialHeight, 0); - } - - int highestX = colorWidth - 1; - int highestZ = colorHeight - 1; - glm::vec3 scale((float)highestX, 1.0f, (float)highestZ); - glm::vec3 center = position * scale; - - glm::vec3 extents = radius * scale; - 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); - int stride = colorWidth * DataBlock::COLOR_BYTES; - uchar* lineDest = (uchar*)colorContents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; - float squaredRadius = extents.x * extents.x; - float multiplierZ = extents.x / extents.z; - char red = color.red(), green = color.green(), blue = color.blue(); - bool changed = false; - for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - if (dx * dx + dz * dz <= squaredRadius) { - dest[0] = red; - dest[1] = green; - dest[2] = blue; - changed = true; - } - } - lineDest += stride; - } - HeightfieldNode* newNode = this; - if (changed) { - newNode = new HeightfieldNode(*this); - newNode->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents))); - } - - highestX = materialWidth - 1; - highestZ = materialHeight - 1; - scale = glm::vec3((float)highestX, 1.0f, (float)highestZ); - center = position * scale; - - extents = radius * scale; - start = glm::floor(center - extents); - end = glm::ceil(center + extents); - - // paint all points within the radius - z = qMax(start.z, 0.0f); - startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); - lineDest = (uchar*)materialContents.data() + (int)z * materialWidth + (int)startX; - squaredRadius = extents.x * extents.x; - multiplierZ = extents.x / extents.z; - uchar materialIndex = getMaterialIndex(material, materials, materialContents); - changed = false; - for (float endZ = qMin(end.z, (float)highestZ); 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) * multiplierZ; - if (dx * dx + dz * dz <= squaredRadius) { - *dest = materialIndex; - changed = true; - } - } - lineDest += materialWidth; - } - if (changed) { - if (newNode == this) { - newNode = new HeightfieldNode(*this); - } - clearUnusedMaterials(materials, materialContents); - newNode->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, - materialContents, materials))); - } - - return newNode; -} - const float HERMITE_GRANULARITY = 1.0f / numeric_limits::max(); void HeightfieldNode::getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, @@ -2158,7 +2020,7 @@ static inline bool isSet(const QVector& stackContents, int stackWidt } HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - Spanner* spanner, const SharedObjectPointer& material, const QColor& color, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, float normalizeScale, float normalizeOffset) { Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); bool intersects = bounds.intersects(spanner->getBounds()); @@ -2172,7 +2034,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, normalizeScale, normalizeOffset); + nextScale, spanner, material, color, paint, normalizeScale, normalizeOffset); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -2262,26 +2124,33 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 start = glm::ceil(transformedBounds.maximum); glm::vec3 end = glm::floor(transformedBounds.minimum); + float stepX = 1.0f, stepZ = 1.0f; + if (paint) { + stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); + stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); + } + float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); float startZ = glm::clamp(start.z, 0.0f, (float)highestHeightZ), endZ = glm::clamp(end.z, 0.0f, (float)highestHeightZ); float voxelStep = scale.x / innerHeightWidth; float voxelScale = scale.y / (numeric_limits::max() * voxelStep); int newTop = start.y * voxelScale; int newBottom = end.y * voxelScale; - glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, newTop / voxelScale, startZ, 1.0f)); - glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + glm::vec3 worldStart = glm::vec3(transform * glm::vec4(startX, paint ? 0.0f : newTop / voxelScale, startZ, 1.0f)); + glm::vec3 worldStepX = glm::vec3(transform * glm::vec4(stepX, 0.0f, 0.0f, 0.0f)); glm::vec3 worldStepY = glm::vec3(transform * glm::vec4(0.0f, 1.0f / voxelScale, 0.0f, 0.0f)); - glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)); + glm::vec3 worldStepZ = glm::vec3(transform * glm::vec4(0.0f, 0.0f, stepZ, 0.0f)); QRgb rgba = color.rgba(); bool erase = (color.alpha() == 0); + uchar materialMaterialIndex = getMaterialIndex(material, newMaterialMaterials, newMaterialContents); uchar stackMaterialIndex = getMaterialIndex(material, newStackMaterials, newStackContents); bool hasOwnColors = spanner->hasOwnColors(); bool hasOwnMaterials = spanner->hasOwnMaterials(); QHash materialMappings; - for (float z = startZ; z >= endZ; z -= 1.0f, worldStart -= worldStepZ) { + for (float z = startZ; z >= endZ; z -= stepZ, worldStart -= worldStepZ) { quint16* heightDest = newHeightContents.data() + (int)z * heightWidth; glm::vec3 worldPos = worldStart; - for (float x = startX; x >= endX; x -= 1.0f, worldPos -= worldStepX) { + for (float x = startX; x >= endX; x -= stepX, worldPos -= worldStepX) { quint16* heightLineDest = heightDest + (int)x; float distance; glm::vec3 normal; @@ -2297,11 +2166,32 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons materialZ <= innerMaterialHeight) ? (newMaterialContents.data() + (int)materialZ * materialWidth + (int)materialX) : NULL; + if (paint && *heightLineDest != 0 && spanner->contains(worldPos + worldStepY * (*heightLineDest * voxelScale))) { + colorDest[0] = qRed(rgba); + colorDest[1] = qGreen(rgba); + colorDest[2] = qBlue(rgba); + *materialDest = materialMaterialIndex; + } + float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; float stackZ = (z - HeightfieldHeight::HEIGHT_BORDER) * innerStackHeight / innerHeightHeight; if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; + if (paint) { + if (stackDest->isEmpty()) { + continue; + } + glm::vec3 pos = worldPos + worldStepY * (float)stackDest->getPosition(); + for (StackArray::Entry* entryDest = stackDest->getEntryData(), *end = entryDest + + stackDest->getEntryCount(); entryDest != end; entryDest++, pos += worldStepY) { + if (entryDest->isSet() && spanner->contains(pos)) { + entryDest->color = rgba; + entryDest->material = stackMaterialIndex; + } + } + continue; + } int prepend = 0, append = 0; if (!stackDest->isEmpty()) { int oldBottom = stackDest->getPosition(); @@ -2479,7 +2369,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons clearUnusedMaterials(newMaterialMaterials, newMaterialContents); clearUnusedMaterials(newStackMaterials, newStackContents); - return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + return new HeightfieldNode(paint ? _height : HeightfieldHeightPointer( + new HeightfieldHeight(heightWidth, newHeightContents)), HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, newMaterialContents, newMaterialMaterials)), HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); @@ -3212,19 +3103,6 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& getScale() * _aspectZ), origin, direction, distance); } -Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& color) { - glm::vec3 inverseScale(1.0f / getScale(), 1.0f, 1.0f / (getScale() * _aspectZ)); - HeightfieldNode* newRoot = _root->paintMaterial(glm::inverse(getRotation()) * (position - getTranslation()) * - inverseScale, radius * inverseScale, material, color); - if (_root == newRoot) { - return this; - } - Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); - return newHeightfield; -} - Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { // first see if we're going to exceed the range limits float minimumValue = 1.0f, maximumValue = numeric_limits::max(); @@ -3241,19 +3119,22 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float } Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color) { - // first see if we're going to exceed the range limits + const QColor& color, bool paint) { + // first see if we're going to exceed the range limits, normalizing if necessary Spanner* spannerData = static_cast(spanner.data()); - float minimumValue = 1.0f, maximumValue = numeric_limits::max(); - _root->getRangeAfterEdit(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), spannerData->getBounds(), minimumValue, maximumValue); - - // normalize if necessary - float normalizeScale, normalizeOffset; - Heightfield* newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); + float normalizeScale = 1.0f, normalizeOffset = 0.0f; + Heightfield* newHeightfield; + if (paint) { + newHeightfield = static_cast(clone(true)); + } else { + float minimumValue = 1.0f, maximumValue = numeric_limits::max(); + _root->getRangeAfterEdit(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + getScale() * _aspectZ), spannerData->getBounds(), minimumValue, maximumValue); + newHeightfield = prepareEdit(minimumValue, maximumValue, normalizeScale, normalizeOffset); + } newHeightfield->setRoot(HeightfieldNodePointer(_root->setMaterial(newHeightfield->getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * newHeightfield->getAspectY(), getScale() * _aspectZ), spannerData, - material, color, normalizeScale, normalizeOffset))); + material, color, paint, normalizeScale, normalizeOffset))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 37af85b584..391aef4a50 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -73,19 +73,14 @@ public: /// Finds the intersection between the described ray and this spanner. virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - /// Attempts to paint on the spanner. - /// \return the modified spanner, or this if no modification was performed - 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); - /// Attempts to "sculpt" with the supplied spanner. + /// Attempts to "sculpt" or "paint" 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); + const QColor& color, bool paint); /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -686,9 +681,6 @@ public: bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material, - const QColor& color); - void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const; @@ -699,7 +691,7 @@ public: 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, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, float normalizeScale, float normalizeOffset); void read(HeightfieldStreamState& state); @@ -797,13 +789,10 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - 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); virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color); + const QColor& color, bool paint); virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; From 78c4bcd2ecdd3844a9fc31ceecff0cd2fc246678 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 6 Jan 2015 15:39:06 -0800 Subject: [PATCH 32/67] Heightfield conversion Hermite bits. --- libraries/metavoxels/src/Spanner.cpp | 40 +++++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 15455bf251..79c9eb1861 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2217,11 +2217,35 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } if (*heightLineDest != 0) { float voxelHeight = *heightLineDest * voxelScale; + const quint16* oldHeightLineDest = _height->getContents().constData() + (int)z * heightWidth + (int)x; + float left = oldHeightLineDest[-1] * voxelScale; + float right = oldHeightLineDest[1] * voxelScale; + float down = oldHeightLineDest[-heightWidth] * voxelScale; + float up = oldHeightLineDest[heightWidth] * voxelScale; + float deltaX = (left == 0.0f || right == 0.0f) ? 0.0f : (left - right); + float deltaZ = (up == 0.0f || down == 0.0f) ? 0.0f : (down - up); for (int i = 0, total = prepend + append; i < total; i++) { int offset = (i < prepend) ? i : stackDest->getEntryCount() - append + (i - prepend); int y = stackDest->getPosition() + offset; - if (y <= voxelHeight) { - StackArray::Entry* entryDest = stackDest->getEntryData() + offset; + StackArray::Entry* entryDest = stackDest->getEntryData() + offset; + if (y > voxelHeight) { + if (y <= right) { + entryDest->setHermiteX(glm::normalize(glm::vec3(voxelHeight - right, 1.0f, deltaZ * 0.5f)), + (right == voxelHeight) ? 0.5f : (y - voxelHeight) / (right - voxelHeight)); + } + if (y <= up) { + entryDest->setHermiteZ(glm::normalize(glm::vec3(deltaX * 0.5f, 1.0f, voxelHeight - up)), + (up == voxelHeight) ? 0.5f : (y - voxelHeight) / (up - voxelHeight)); + } + } else { + if (right != 0.0f && y > right) { + entryDest->setHermiteX(glm::normalize(glm::vec3(voxelHeight - right, 1.0f, deltaZ * 0.5f)), + (right == voxelHeight) ? 0.5f : (y - voxelHeight) / (right - voxelHeight)); + } + if (up != 0.0f && y > up) { + entryDest->setHermiteZ(glm::normalize(glm::vec3(deltaX * 0.5f, 1.0f, voxelHeight - up)), + (up == voxelHeight) ? 0.5f : (y - voxelHeight) / (up - voxelHeight)); + } if (colorDest) { entryDest->color = qRgb(colorDest[0], colorDest[1], colorDest[2]); } @@ -2239,17 +2263,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } if (y + 1 > voxelHeight) { *heightLineDest = 0; - - const quint16* oldHeightLineDest = _height->getContents().constData() + - (int)z * heightWidth + (int)x; - quint16 left = oldHeightLineDest[-1]; - quint16 right = oldHeightLineDest[1]; - quint16 up = oldHeightLineDest[heightWidth]; - quint16 down = oldHeightLineDest[-heightWidth]; - - entryDest->setHermiteY(glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : - (left - right), 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : - (down - up))), voxelHeight - y); + entryDest->setHermiteY(glm::normalize(glm::vec3(deltaX, 2.0f, deltaZ)), voxelHeight - y); } } } From 48fb9a27baaa1e925f49b9a7d3b8f276a1ca64b2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 6 Jan 2015 17:55:04 -0800 Subject: [PATCH 33/67] Progress on heightfield/voxel interface. --- interface/src/MetavoxelSystem.cpp | 182 +++++++++++++-------------- libraries/metavoxels/src/Spanner.cpp | 2 +- 2 files changed, 91 insertions(+), 93 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index f7ee827524..d03e37b5a4 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1645,6 +1645,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // find the y extents of this and the neighboring columns int minimumY = INT_MAX, maximumY = -1; lineSrc->getExtents(minimumY, maximumY); + int localMinimumY = minimumY, localMaximumY = maximumY; if (middleX) { lineSrc[1].getExtents(minimumY, maximumY); if (middleZ) { @@ -1654,10 +1655,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (middleZ) { lineSrc[stackWidth].getExtents(minimumY, maximumY); } - if (maximumY >= minimumY) { - int position = minimumY; - int count = maximumY - minimumY + 1; + int position = minimumY - 1; + int count = maximumY - minimumY + 2; NormalIndex lastIndexY; indicesX.position = position; indicesX.resize(count); @@ -1935,104 +1935,102 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the first x, y, and z are repeated for the boundary edge; past that, we consider generating // quads for each edge that includes a transition, using indices of previously generated vertices - if (x != 0 && z != 0) { - int reclampedX = qMin(clampedX, stackWidth - 1); - int reclampedZ = qMin(clampedZ, stackHeight - 1); - if (alpha0 != alpha1) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + int reclampedX = qMin(clampedX, stackWidth - 1); + int reclampedZ = qMin(clampedZ, stackHeight - 1); + if (alpha0 != alpha1 && y >= localMinimumY && y <= localMaximumY && z != 0) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (y > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + } + if (reclampedZ > 0) { if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); } - if (reclampedZ > 0) { - if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); - } - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); - } - - const NormalIndex& index1 = lastIndexY; - const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); - const NormalIndex& index3 = lastIndicesZ[x].get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative x - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive x - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - if (alpha0 != alpha2) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); - } - } + const NormalIndex& index1 = lastIndexY; + const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); + const NormalIndex& index3 = lastIndicesZ[x].get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative x + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive x + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); + } + + if (alpha0 != alpha2 && x != 0 && z != 0) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); } - - const NormalIndex& index1 = lastIndicesZ[x].get(y); - const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); - const NormalIndex& index3 = lastIndicesX.get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, - vertices.at(index1.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative y - indices.append(index3.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive y - indices.append(index1.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); + } + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - if (alpha0 != alpha4) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - if (y > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); - } - } - if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - } - - const NormalIndex& index1 = lastIndexY; - const NormalIndex& index2 = lastIndicesX.get(y - 1); - const NormalIndex& index3 = lastIndicesX.get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative z - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } else { // quad faces positive z - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } - indices.append(index.getClosestIndex(normal, vertices)); + const NormalIndex& index1 = lastIndicesZ[x].get(y); + const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); + const NormalIndex& index3 = lastIndicesX.get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, + vertices.at(index1.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative y + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive y + indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); } + indices.append(index.getClosestIndex(normal, vertices)); + } + + if (alpha0 != alpha4 && x != 0 && y >= localMinimumY && y <= localMaximumY) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); + if (y > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); + } + } + if (y > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + } + + const NormalIndex& index1 = lastIndexY; + const NormalIndex& index2 = lastIndicesX.get(y - 1); + const NormalIndex& index3 = lastIndicesX.get(y); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative z + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } else { // quad faces positive z + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } lastIndexY = index; indicesX[y - position] = index; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 79c9eb1861..56a3e2b6a2 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2179,7 +2179,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (stackX >= 0.0f && stackX <= innerStackWidth && stackZ >= 0.0f && stackZ <= innerStackHeight) { StackArray* stackDest = newStackContents.data() + (int)stackZ * stackWidth + (int)stackX; if (paint) { - if (stackDest->isEmpty()) { + if (stackDest->isEmpty() || glm::fract(x) != 0.0f || glm::fract(z) != 0.0f) { continue; } glm::vec3 pos = worldPos + worldStepY * (float)stackDest->getPosition(); From 1abf111d7f37eb3f684b305b647179ddf57601f4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 7 Jan 2015 11:19:40 -0800 Subject: [PATCH 34/67] Index fix. --- interface/src/MetavoxelSystem.cpp | 166 +++++++++++++++--------------- 1 file changed, 81 insertions(+), 85 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d03e37b5a4..d32f4507f3 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1439,7 +1439,7 @@ public: bool NormalIndex::isValid() const { for (int i = 0; i < MAX_NORMALS_PER_VERTEX; i++) { - if (indices[i] != 0) { + if (indices[i] >= 0) { return true; } } @@ -1482,9 +1482,9 @@ public: }; const NormalIndex& IndexVector::get(int y) const { - static NormalIndex nullIndex = { { 0, 0, 0, 0 } }; + static NormalIndex invalidIndex = { { -1, -1, -1, -1 } }; int relative = y - position; - return (relative >= 0 && relative < size()) ? at(relative) : nullIndex; + return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; } void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, @@ -1645,7 +1645,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // find the y extents of this and the neighboring columns int minimumY = INT_MAX, maximumY = -1; lineSrc->getExtents(minimumY, maximumY); - int localMinimumY = minimumY, localMaximumY = maximumY; if (middleX) { lineSrc[1].getExtents(minimumY, maximumY); if (middleZ) { @@ -1656,9 +1655,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g lineSrc[stackWidth].getExtents(minimumY, maximumY); } if (maximumY >= minimumY) { - int position = minimumY - 1; - int count = maximumY - minimumY + 2; - NormalIndex lastIndexY; + int position = minimumY; + int count = maximumY - minimumY + 1; + NormalIndex lastIndexY = { { -1, -1, -1, -1 } }; indicesX.position = position; indicesX.resize(count); indicesZ[x].position = position; @@ -1937,105 +1936,101 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // quads for each edge that includes a transition, using indices of previously generated vertices int reclampedX = qMin(clampedX, stackWidth - 1); int reclampedZ = qMin(clampedZ, stackHeight - 1); - if (alpha0 != alpha1 && y >= localMinimumY && y <= localMaximumY && z != 0) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - } - if (reclampedZ > 0) { - if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); - } - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); - } - + if (alpha0 != alpha1) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); const NormalIndex& index3 = lastIndicesZ[x].get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative x - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive x - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); + if (index1.isValid() && index2.isValid() && index3.isValid()) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); + } + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative x + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive x + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } - indices.append(index.getClosestIndex(normal, vertices)); } - if (alpha0 != alpha2 && x != 0 && z != 0) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); - } - } - if (reclampedZ > 0) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); - } - + if (alpha0 != alpha2) { const NormalIndex& index1 = lastIndicesZ[x].get(y); const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); const NormalIndex& index3 = lastIndicesX.get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, - vertices.at(index1.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative y - indices.append(index3.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); - } else { // quad faces positive y - indices.append(index1.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); + if (index1.isValid() && index2.isValid() && index3.isValid()) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ - 1), indices.size()); + } + } + if (reclampedZ > 0) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); + } + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, + vertices.at(index1.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative y + indices.append(index3.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } else { // quad faces positive y + indices.append(index1.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } - indices.append(index.getClosestIndex(normal, vertices)); } - if (alpha0 != alpha4 && x != 0 && y >= localMinimumY && y <= localMaximumY) { - quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); - if (reclampedX > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); - if (y > 0) { - quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); - } - } - if (y > 0) { - quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - } - + if (alpha0 != alpha4) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesX.get(y - 1); const NormalIndex& index3 = lastIndicesX.get(y); - - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - - if (alpha0 == 0) { // quad faces negative z - indices.append(index1.getClosestIndex(normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index3.getClosestIndex(normal, vertices)); - } else { // quad faces positive z - indices.append(index3.getClosestIndex(normal = -normal, vertices)); - indices.append(index2.getClosestIndex(normal, vertices)); - indices.append(index1.getClosestIndex(normal, vertices)); + if (index1.isValid() && index2.isValid() && index3.isValid()) { + quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); + if (reclampedX > 0) { + quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); + quadIndices.insert(qRgb(reclampedX - 1, y - 1, reclampedZ), indices.size()); + } + quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); + + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, + vertices.at(index3.indices[0]).vertex - first); + + if (alpha0 == 0) { // quad faces negative z + indices.append(index1.getClosestIndex(normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index3.getClosestIndex(normal, vertices)); + } else { // quad faces positive z + indices.append(index3.getClosestIndex(normal = -normal, vertices)); + indices.append(index2.getClosestIndex(normal, vertices)); + indices.append(index1.getClosestIndex(normal, vertices)); + } + indices.append(index.getClosestIndex(normal, vertices)); } - indices.append(index.getClosestIndex(normal, vertices)); } lastIndexY = index; indicesX[y - position] = index; indicesZ[x][y - position] = index; } + } else { + indicesX.clear(); + indicesZ[x].clear(); } if (x != 0) { lineSrc++; @@ -2046,6 +2041,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g src += stackWidth; } indicesZ.swap(lastIndicesZ); + lastIndicesX.clear(); } _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth, node->getStack()->getMaterials()); From 4c3163fae8590e4886665a020d49b1c8e907a425 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 7 Jan 2015 17:24:26 -0800 Subject: [PATCH 35/67] Closer to working stitching. --- interface/src/MetavoxelSystem.cpp | 414 +++++++++++++++++---------- libraries/metavoxels/src/Spanner.cpp | 96 +++---- libraries/metavoxels/src/Spanner.h | 10 +- 3 files changed, 321 insertions(+), 199 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d32f4507f3..9755b884d3 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1623,7 +1623,34 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g int stackWidth = node->getStack()->getWidth(); int stackHeight = node->getStack()->getContents().size() / stackWidth; int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; const StackArray* src = node->getStack()->getContents().constData(); + const quint16* heightSrc = node->getHeight()->getContents().constData() + + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; + QVector stackMaterials = node->getStack()->getMaterials(); + QHash materialMap; + + int colorWidth; + const uchar* colorSrc = NULL; + float colorStepX, colorStepZ; + if (node->getColor()) { + colorWidth = node->getColor()->getWidth(); + int colorHeight = node->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + colorSrc = (const uchar*)node->getColor()->getContents().constData(); + colorStepX = (colorWidth - HeightfieldData::SHARED_EDGE) / (float)innerStackWidth; + colorStepZ = (colorHeight - HeightfieldData::SHARED_EDGE) / (float)innerStackHeight; + } + + int materialWidth; + const uchar* materialSrc = NULL; + float materialStepX, materialStepZ; + if (node->getMaterial()) { + materialWidth = node->getMaterial()->getWidth(); + int materialHeight = node->getMaterial()->getContents().size() / materialWidth; + materialSrc = (const uchar*)node->getMaterial()->getContents().constData(); + materialStepX = (materialWidth - HeightfieldData::SHARED_EDGE) / (float)innerStackWidth; + materialStepZ = (materialHeight - HeightfieldData::SHARED_EDGE) / (float)innerStackHeight; + } const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; @@ -1634,11 +1661,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g IndexVector lastIndicesX; QVector indicesZ(stackWidth + 1); QVector lastIndicesZ(stackWidth + 1); - float scale = 1.0f / innerStackWidth; + float step = 1.0f / innerStackWidth; + float voxelScale = scale.y / (numeric_limits::max() * scale.x * step); for (int z = 0; z <= stackHeight; z++) { bool middleZ = (z != 0 && z != stackHeight); const StackArray* lineSrc = src; + const quint16* heightLineSrc = heightSrc; for (int x = 0; x <= stackWidth; x++) { bool middleX = (x != 0 && x != stackWidth); @@ -1662,32 +1691,36 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g indicesX.resize(count); indicesZ[x].position = position; indicesZ[x].resize(count); + float heightfieldHeight = *heightLineSrc * voxelScale; + float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale; + float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale; + float nextHeightfieldHeightXZ = heightLineSrc[width + 1] * voxelScale; for (int y = position, end = position + count; y < end; y++) { - const StackArray::Entry& entry = lineSrc->getEntry(y); + const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (displayHermite && x != 0 && z != 0) { glm::vec3 normal; if (entry.hermiteX != 0) { - glm::vec3 start = glm::vec3(clampedX + entry.getHermiteX(normal), y, clampedZ) * scale; + glm::vec3 start = glm::vec3(clampedX + entry.getHermiteX(normal), y, clampedZ) * step; hermiteSegments.append(start); - hermiteSegments.append(start + normal * scale); + hermiteSegments.append(start + normal * step); } if (entry.hermiteY != 0) { - glm::vec3 start = glm::vec3(clampedX, y + entry.getHermiteY(normal), clampedZ) * scale; + glm::vec3 start = glm::vec3(clampedX, y + entry.getHermiteY(normal), clampedZ) * step; hermiteSegments.append(start); - hermiteSegments.append(start + normal * scale); + hermiteSegments.append(start + normal * step); } if (entry.hermiteZ != 0) { - glm::vec3 start = glm::vec3(clampedX, y, clampedZ + entry.getHermiteZ(normal)) * scale; + glm::vec3 start = glm::vec3(clampedX, y, clampedZ + entry.getHermiteZ(normal)) * step; hermiteSegments.append(start); - hermiteSegments.append(start + normal * scale); + hermiteSegments.append(start + normal * step); } } // number variables correspond to cube corners, where the x, y, and z components are represented as // bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at the minimum x, y, and // z corner and alpha7 is the value at the maximum x, y, and z - int alpha0 = qAlpha(entry.color); - int alpha2 = lineSrc->getEntryAlpha(y + 1); + int alpha0 = lineSrc->getEntryAlpha(y, heightfieldHeight); + int alpha2 = lineSrc->getEntryAlpha(y + 1, heightfieldHeight); int alpha1 = alpha0, alpha3 = alpha2, alpha4 = alpha0, alpha6 = alpha2; int alphaTotal = alpha0 + alpha2; int possibleTotal = 2 * numeric_limits::max(); @@ -1695,25 +1728,25 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between // neighboring blocks, which share only one layer of points if (middleZ) { - alphaTotal += (alpha4 = lineSrc[stackWidth].getEntryAlpha(y)); + alphaTotal += (alpha4 = lineSrc[stackWidth].getEntryAlpha(y, nextHeightfieldHeightZ)); possibleTotal += numeric_limits::max(); - alphaTotal += (alpha6 = lineSrc[stackWidth].getEntryAlpha(y + 1)); + alphaTotal += (alpha6 = lineSrc[stackWidth].getEntryAlpha(y + 1, nextHeightfieldHeightZ)); possibleTotal += numeric_limits::max(); } int alpha5 = alpha4, alpha7 = alpha6; if (middleX) { - alphaTotal += (alpha1 = lineSrc[1].getEntryAlpha(y)); + alphaTotal += (alpha1 = lineSrc[1].getEntryAlpha(y, nextHeightfieldHeightX)); possibleTotal += numeric_limits::max(); - alphaTotal += (alpha3 = lineSrc[1].getEntryAlpha(y + 1)); + alphaTotal += (alpha3 = lineSrc[1].getEntryAlpha(y + 1, nextHeightfieldHeightX)); possibleTotal += numeric_limits::max(); if (middleZ) { - alphaTotal += (alpha5 = lineSrc[stackWidth + 1].getEntryAlpha(y)); + alphaTotal += (alpha5 = lineSrc[stackWidth + 1].getEntryAlpha(y, nextHeightfieldHeightXZ)); possibleTotal += numeric_limits::max(); - alphaTotal += (alpha7 = lineSrc[stackWidth + 1].getEntryAlpha(y + 1)); + alphaTotal += (alpha7 = lineSrc[stackWidth + 1].getEntryAlpha(y + 1, nextHeightfieldHeightXZ)); possibleTotal += numeric_limits::max(); } } @@ -1723,10 +1756,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded int crossingCount = 0; - const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1); + const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight); if (middleX) { - const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y); - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1); + const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX); + const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); if (alpha0 != alpha1) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); @@ -1743,9 +1776,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y); - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(y); - const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry(y + 1); + const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); + const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( + y, nextHeightfieldHeightXZ); + const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry( + y + 1, nextHeightfieldHeightXZ); if (alpha1 != alpha5) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); @@ -1753,7 +1788,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha3 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1); + const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); } @@ -1764,13 +1799,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha5 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(y); + const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( + y, nextHeightfieldHeightXZ); crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); } if (alpha6 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1); + const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry( + y + 1, nextHeightfieldHeightZ); crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); } @@ -1782,8 +1819,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y); - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1); + const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); + const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, nextHeightfieldHeightZ); if (alpha0 != alpha4) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); @@ -1800,136 +1837,222 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ); } } - glm::vec3 center; - glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; - int normalCount = 0; - const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); - const int MAX_MATERIALS_PER_VERTEX = 4; - quint8 materials[] = { 0, 0, 0, 0 }; - glm::vec4 materialWeights; - float totalWeight = 0.0f; - int red = 0, green = 0, blue = 0; + // determine whether we need to stitch into surrounding heightfield + bool stitch = false; for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - center += crossing.point; - - int j = 0; - for (; j < normalCount; j++) { - if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { - normals[j] = safeNormalize(normals[j] + crossing.normal); - break; + if (qAlpha(crossings[i].color) == 0) { + stitch = true; + break; + } + } + NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; + if (stitch) { + // pack the corners set into a set of flags + int corners = 0; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + if (qAlpha(crossing.color) == 0) { + int corner = (crossing.point.x == 1.0f) | ((crossing.point.z == 1.0f) << 1); + corners |= (1 << corner); } } - if (j == normalCount) { - normals[normalCount++] = crossing.normal; + // fall into four cases based on set corners + glm::vec3 center; + int offsetX = 0, offsetZ = 0; + switch(corners) { + case 3: + case 11: // z- edge, x+/z- corner + center = glm::vec3(1.0f, nextHeightfieldHeightX, 0.0f); + offsetX = 1; + break; + + case 12: + case 13: // z+ edge, x-/z+ corner + center = glm::vec3(0.0f, nextHeightfieldHeightZ, 1.0f); + offsetZ = 1; + break; + + case 10: + case 14: // x+ edge, x+/z+ corner + center = glm::vec3(1.0f, nextHeightfieldHeightXZ, 1.0f); + offsetX = offsetZ = 1; + break; + + default: // x- edge, x-/z- corner + center = glm::vec3(0.0f, heightfieldHeight, 0.0f); + break; } + const quint16* height = heightLineSrc + width * offsetZ + offsetX; + int left = height[-1]; + int right = height[1]; + int down = height[-width]; + int up = height[width]; + glm::vec3 normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right, + 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up)); + quint8 red = numeric_limits::max(); + quint8 green = numeric_limits::max(); + quint8 blue = numeric_limits::max(); + if (colorSrc) { + const uchar* color = colorSrc + ((int)(clampedZ * colorStepZ) * colorWidth + + (int)(clampedX * colorStepX)) * DataBlock::COLOR_BYTES; + red = color[0]; + green = color[1]; + blue = color[2]; + } + quint8 material = 0; + if (materialSrc) { + material = materialSrc[(int)(clampedZ * materialStepZ) * materialWidth + + (int)(clampedX * materialStepX)]; + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + mapping = getMaterialIndex(node->getMaterial()->getMaterials().at(material - 1), + stackMaterials); + } + material = mapping; + } + } + VoxelPoint point = { glm::vec3(clampedX + offsetX, *height * voxelScale, + clampedZ + offsetZ) * step, { red, green, blue }, + { (char)(normal.x * numeric_limits::max()), + (char)(normal.y * numeric_limits::max()), + (char)(normal.z * numeric_limits::max()) }, { material, 0, 0, 0 }, + { numeric_limits::max(), 0, 0, 0 } }; + vertices.append(point); - red += qRed(crossing.color); - green += qGreen(crossing.color); - blue += qBlue(crossing.color); - - // when assigning a material, search for its presence and, if not found, - // place it in the first empty slot - if (crossing.material != 0) { - for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { - if (materials[j] == crossing.material) { - materialWeights[j] += 1.0f; - totalWeight += 1.0f; - break; - - } else if (materials[j] == 0) { - materials[j] = crossing.material; - materialWeights[j] = 1.0f; - totalWeight += 1.0f; + } else { + glm::vec3 center; + glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; + int normalCount = 0; + const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); + const int MAX_MATERIALS_PER_VERTEX = 4; + quint8 materials[] = { 0, 0, 0, 0 }; + glm::vec4 materialWeights; + float totalWeight = 0.0f; + int red = 0, green = 0, blue = 0; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + center += crossing.point; + + int j = 0; + for (; j < normalCount; j++) { + if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { + normals[j] = safeNormalize(normals[j] + crossing.normal); break; } } - } - } - center /= crossingCount; - - // use a sequence of Givens rotations to perform a QR decomposition - // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf - glm::mat4 r(0.0f); - glm::vec4 bottom; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); - - for (int j = 0; j < 4; j++) { - float angle = glm::atan(-bottom[j], r[j][j]); - float sina = glm::sin(angle); - float cosa = glm::cos(angle); + if (j == normalCount) { + normals[normalCount++] = crossing.normal; + } - for (int k = 0; k < 4; k++) { - float tmp = bottom[k]; - bottom[k] = sina * r[k][j] + cosa * tmp; - r[k][j] = cosa * r[k][j] - sina * tmp; + red += qRed(crossing.color); + green += qGreen(crossing.color); + blue += qBlue(crossing.color); + + // when assigning a material, search for its presence and, if not found, + // place it in the first empty slot + if (crossing.material != 0) { + for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { + if (materials[j] == crossing.material) { + materialWeights[j] += 1.0f; + totalWeight += 1.0f; + break; + + } else if (materials[j] == 0) { + materials[j] = crossing.material; + materialWeights[j] = 1.0f; + totalWeight += 1.0f; + break; + } + } } } - } - - // extract the submatrices, form ata - glm::mat3 a(r); - glm::vec3 b(r[3]); - glm::mat3 atrans = glm::transpose(a); - glm::mat3 ata = atrans * a; - - // find the eigenvalues and eigenvectors of ata - // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) - glm::mat3 d = ata; - glm::quat combinedRotation; - const int MAX_ITERATIONS = 20; - for (int i = 0; i < MAX_ITERATIONS; i++) { - glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); - int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? - (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); - const float DESIRED_PRECISION = 0.00001f; - if (offDiagonals[largestIndex] < DESIRED_PRECISION) { - break; + center /= crossingCount; + + // use a sequence of Givens rotations to perform a QR decomposition + // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf + glm::mat4 r(0.0f); + glm::vec4 bottom; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); + + for (int j = 0; j < 4; j++) { + float angle = glm::atan(-bottom[j], r[j][j]); + float sina = glm::sin(angle); + float cosa = glm::cos(angle); + + for (int k = 0; k < 4; k++) { + float tmp = bottom[k]; + bottom[k] = sina * r[k][j] + cosa * tmp; + r[k][j] = cosa * r[k][j] - sina * tmp; + } + } } - int largestJ = (largestIndex == 2) ? 1 : 0; - int largestI = (largestIndex == 0) ? 1 : 2; - float sjj = d[largestJ][largestJ]; - float sii = d[largestI][largestI]; - float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; - glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : - (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); - combinedRotation = glm::normalize(rotation * combinedRotation); - glm::mat3 matrix = glm::mat3_cast(combinedRotation); - d = matrix * ata * glm::transpose(matrix); - } + + // extract the submatrices, form ata + glm::mat3 a(r); + glm::vec3 b(r[3]); + glm::mat3 atrans = glm::transpose(a); + glm::mat3 ata = atrans * a; + + // find the eigenvalues and eigenvectors of ata + // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) + glm::mat3 d = ata; + glm::quat combinedRotation; + const int MAX_ITERATIONS = 20; + for (int i = 0; i < MAX_ITERATIONS; i++) { + glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); + int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? + (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); + const float DESIRED_PRECISION = 0.00001f; + if (offDiagonals[largestIndex] < DESIRED_PRECISION) { + break; + } + int largestJ = (largestIndex == 2) ? 1 : 0; + int largestI = (largestIndex == 0) ? 1 : 2; + float sjj = d[largestJ][largestJ]; + float sii = d[largestI][largestI]; + float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; + glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : + (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); + combinedRotation = glm::normalize(rotation * combinedRotation); + glm::mat3 matrix = glm::mat3_cast(combinedRotation); + d = matrix * ata * glm::transpose(matrix); + } + + // form the singular matrix from the eigenvalues + const float MIN_SINGULAR_THRESHOLD = 0.1f; + d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; + d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; + d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; + + // compute the pseudo-inverse, ataplus, and use to find the minimizing solution + glm::mat3 u = glm::mat3_cast(combinedRotation); + glm::mat3 ataplus = glm::transpose(u) * d * u; + glm::vec3 solution = (ataplus * atrans * b) + center; + + // make sure it doesn't fall beyond the cell boundaries + center = glm::clamp(solution, 0.0f, 1.0f); - // form the singular matrix from the eigenvalues - const float MIN_SINGULAR_THRESHOLD = 0.1f; - d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; - d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; - d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; - - // compute the pseudo-inverse, ataplus, and use to find the minimizing solution - glm::mat3 u = glm::mat3_cast(combinedRotation); - glm::mat3 ataplus = glm::transpose(u) * d * u; - glm::vec3 solution = (ataplus * atrans * b) + center; - - // make sure it doesn't fall beyond the cell boundaries - center = glm::clamp(solution, 0.0f, 1.0f); - - if (totalWeight > 0.0f) { - materialWeights *= (numeric_limits::max() / totalWeight); - } - VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * scale, - { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, - { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, - { materials[0], materials[1], materials[2], materials[3] }, - { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], - (quint8)materialWeights[3] } }; - - NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; - vertices.append(point); - for (int i = 1; i < normalCount; i++) { - index.indices[i] = vertices.size(); - point.setNormal(normals[i]); + if (totalWeight > 0.0f) { + materialWeights *= (numeric_limits::max() / totalWeight); + } + VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * step, + { (quint8)(red / crossingCount), (quint8)(green / crossingCount), + (quint8)(blue / crossingCount) }, + { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), + (char)(normals[0].z * 127.0f) }, + { materials[0], materials[1], materials[2], materials[3] }, + { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], + (quint8)materialWeights[3] } }; + vertices.append(point); + for (int i = 1; i < normalCount; i++) { + index.indices[i] = vertices.size(); + point.setNormal(normals[i]); + vertices.append(point); + } } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating @@ -2034,17 +2157,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (x != 0) { lineSrc++; + heightLineSrc++; } indicesX.swap(lastIndicesX); } if (z != 0) { src += stackWidth; + heightSrc += width; } indicesZ.swap(lastIndicesZ); lastIndicesX.clear(); } - _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth, - node->getStack()->getMaterials()); + _voxels = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, stackWidth, stackMaterials); } if (_voxels) { diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 56a3e2b6a2..29015f998d 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1119,18 +1119,7 @@ MaterialObject::MaterialObject() : _scaleT(1.0f) { } -static QHash countIndices(const QByteArray& contents) { - QHash counts; - for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { - if (*src != 0) { - counts[*src]++; - } - } - return counts; -} - -static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, - QByteArray& contents) { +int getMaterialIndex(const SharedObjectPointer& material, QVector& materials) { if (!(material && static_cast(material.data())->getDiffuse().isValid())) { return 0; } @@ -1155,6 +1144,25 @@ static uchar getMaterialIndex(const SharedObjectPointer& material, QVector countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + +static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, + QByteArray& contents) { + int index = getMaterialIndex(material, materials); + if (index != -1) { + return index; + } // last resort: find the least-used material and remove it QHash counts = countIndices(contents); uchar materialIndex = 0; @@ -1199,30 +1207,10 @@ static QHash countIndices(const QVector& contents) { static uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QVector& contents) { - if (!(material && static_cast(material.data())->getDiffuse().isValid())) { - return 0; - } - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& existingMaterial = materials.at(i); - if (existingMaterial) { - if (existingMaterial->equals(material.data())) { - return i + 1; - } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; - } - } - // if nothing found, use the first empty slot or append - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = material; - return firstEmptyIndex + 1; - } - if (materials.size() < numeric_limits::max()) { - materials.append(material); - return materials.size(); - } + int index = getMaterialIndex(material, materials); + if (index != -1) { + return index; + } // last resort: find the least-used material and remove it QHash counts = countIndices(contents); uchar materialIndex = 0; @@ -1353,33 +1341,39 @@ float StackArray::Entry::getHermiteZ(glm::vec3& normal) const { return getHermite(hermiteZ, normal); } -int StackArray::getEntryAlpha(int y) const { +int StackArray::getEntryAlpha(int y, float heightfieldHeight) const { int count = getEntryCount(); - if (count == 0) { - return 0; + if (count != 0) { + int relative = y - getPosition(); + if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { + return qAlpha(getEntryData()[qMax(relative, 0)].color); + } } - int relative = y - getPosition(); - return (relative < count) ? qAlpha(getEntryData()[qMax(relative, 0)].color) : 0; + return (heightfieldHeight != 0.0f && y <= heightfieldHeight) ? numeric_limits::max() : 0; } -StackArray::Entry& StackArray::getEntry(int y) { +StackArray::Entry& StackArray::getEntry(int y, float heightfieldHeight) { static Entry emptyEntry; int count = getEntryCount(); - if (count == 0) { - return emptyEntry; + if (count != 0) { + int relative = y - getPosition(); + if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { + return getEntryData()[qMax(relative, 0)]; + } } - int relative = y - getPosition(); - return (relative < count) ? getEntryData()[qMax(relative, 0)] : emptyEntry; + return emptyEntry; } -const StackArray::Entry& StackArray::getEntry(int y) const { +const StackArray::Entry& StackArray::getEntry(int y, float heightfieldHeight) const { static Entry emptyEntry; int count = getEntryCount(); - if (count == 0) { - return emptyEntry; + if (count != 0) { + int relative = y - getPosition(); + if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f || y < heightfieldHeight)) { + return getEntryData()[qMax(relative, 0)]; + } } - int relative = y - getPosition(); - return (relative < count) ? getEntryData()[qMax(relative, 0)] : emptyEntry; + return emptyEntry; } void StackArray::getExtents(int& minimumY, int& maximumY) const { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 391aef4a50..224bd749a9 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -522,6 +522,10 @@ private: float _scaleT; }; +/// Finds a material index for the supplied material in the provided list, adding an entry if necessary. Returns -1 +/// on failure (no room to add new material). +int getMaterialIndex(const SharedObjectPointer& material, QVector& materials); + typedef QExplicitlySharedDataPointer HeightfieldStackPointer; /// A single column within a stack block. @@ -573,10 +577,10 @@ public: Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } - int getEntryAlpha(int y) const; + int getEntryAlpha(int y, float heightfieldHeight) const; - Entry& getEntry(int y); - const Entry& getEntry(int y) const; + Entry& getEntry(int y, float heightfieldHeight); + const Entry& getEntry(int y, float heightfieldHeight) const; void getExtents(int& minimumY, int& maximumY) const; From ec327396897b55897b32703e77f8d9972ca5a7b4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 Jan 2015 12:52:52 -0800 Subject: [PATCH 36/67] CSG improvements. --- interface/src/MetavoxelSystem.cpp | 8 ----- libraries/metavoxels/src/Spanner.cpp | 44 ++++++++++++++++------------ 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9755b884d3..ffa23f1b2d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1857,30 +1857,22 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } // fall into four cases based on set corners - glm::vec3 center; int offsetX = 0, offsetZ = 0; switch(corners) { case 3: case 11: // z- edge, x+/z- corner - center = glm::vec3(1.0f, nextHeightfieldHeightX, 0.0f); offsetX = 1; break; case 12: case 13: // z+ edge, x-/z+ corner - center = glm::vec3(0.0f, nextHeightfieldHeightZ, 1.0f); offsetZ = 1; break; case 10: case 14: // x+ edge, x+/z+ corner - center = glm::vec3(1.0f, nextHeightfieldHeightXZ, 1.0f); offsetX = offsetZ = 1; break; - - default: // x- edge, x-/z- corner - center = glm::vec3(0.0f, heightfieldHeight, 0.0f); - break; } const quint16* height = heightLineSrc + width * offsetZ + offsetX; int left = height[-1]; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 29015f998d..d1bb7b6870 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2209,9 +2209,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->setPosition(newBottom); prepend = stackDest->getEntryCount(); } + const quint16* oldHeightLineDest = _height->getContents().constData() + (int)z * heightWidth + (int)x; if (*heightLineDest != 0) { float voxelHeight = *heightLineDest * voxelScale; - const quint16* oldHeightLineDest = _height->getContents().constData() + (int)z * heightWidth + (int)x; float left = oldHeightLineDest[-1] * voxelScale; float right = oldHeightLineDest[1] * voxelScale; float down = oldHeightLineDest[-heightWidth] * voxelScale; @@ -2264,8 +2264,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } StackArray::Entry* entryDest = stackDest->getEntryData() + (newTop - stackDest->getPosition()); glm::vec3 pos = worldPos; + float voxelHeight = *heightLineDest * voxelScale; + float nextVoxelHeightX = heightLineDest[1] * voxelScale; + float nextVoxelHeightZ = heightLineDest[heightWidth] * voxelScale; + float oldVoxelHeight = *oldHeightLineDest * voxelScale; + float oldNextVoxelHeightX = oldHeightLineDest[1] * voxelScale; + float oldNextVoxelHeightZ = oldHeightLineDest[heightWidth] * voxelScale; for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { - bool oldCurrentSet = entryDest->isSet(); + int oldCurrentAlpha = stackDest->getEntryAlpha(y, oldVoxelHeight); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { entryDest->color = spanner->getColorAt(pos); @@ -2289,18 +2295,20 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest->material = stackMaterialIndex; } } - bool currentSet = entryDest->isSet(); + + int currentAlpha = stackDest->getEntryAlpha(y, voxelHeight); + bool flipped = (color.alpha() == currentAlpha); int nextStackX = (int)stackX + 1; if (nextStackX <= innerStackWidth) { - bool nextSetX = isSet(newStackContents, stackWidth, nextStackX, y, (int)stackZ); - if (nextSetX == currentSet) { + int nextAlphaX = newStackContents.at((int)stackZ * stackWidth + nextStackX).getEntryAlpha( + y, nextVoxelHeightX); + if (nextAlphaX == currentAlpha) { entryDest->hermiteX = 0; } else { - bool flipped = (erase == nextSetX); float oldDistance = flipped ? 0.0f : 1.0f; - if (currentSet == oldCurrentSet && nextSetX == isSet(oldStackContents, stackWidth, - nextStackX, y, (int)stackZ)) { + if (currentAlpha == oldCurrentAlpha && nextAlphaX == oldStackContents.at((int)stackZ * stackWidth + + nextStackX).getEntryAlpha(y, oldNextVoxelHeightX) && entryDest->hermiteX != 0) { oldDistance = qAlpha(entryDest->hermiteX) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + worldStepX, pos, distance, normal) && @@ -2311,16 +2319,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } } } - bool nextSetY = (entryDest != stackDest->getEntryData() + stackDest->getEntryCount() - 1 && - (entryDest + 1)->isSet()); - if (nextSetY == currentSet) { + bool nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight); + if (nextAlphaY == currentAlpha) { entryDest->hermiteY = 0; } else { - bool flipped = (erase == nextSetY); float oldDistance = flipped ? 0.0f : 1.0f; - if (currentSet == oldCurrentSet && nextSetY == isSet(oldStackContents, stackWidth, - (int)stackX, y + 1, (int)stackZ)) { + if (currentAlpha == oldCurrentAlpha && nextAlphaY == oldStackContents.at((int)stackZ * stackWidth + + (int)stackX).getEntryAlpha(y + 1, oldVoxelHeight) && entryDest->hermiteY != 0) { oldDistance = qAlpha(entryDest->hermiteY) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + worldStepY, pos, distance, normal) && @@ -2332,15 +2338,15 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } int nextStackZ = (int)stackZ + 1; if (nextStackZ <= innerStackHeight) { - bool nextSetZ = isSet(newStackContents, stackWidth, (int)stackX, y, nextStackZ); - if (nextSetZ == currentSet) { + bool nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha( + y, nextVoxelHeightZ); + if (nextAlphaZ == currentAlpha) { entryDest->hermiteZ = 0; } else { - bool flipped = (erase == nextSetZ); float oldDistance = flipped ? 0.0f : 1.0f; - if (currentSet == oldCurrentSet && nextSetZ == isSet(oldStackContents, stackWidth, - (int)stackX, y, nextStackZ)) { + if (currentAlpha == oldCurrentAlpha && nextAlphaZ == oldStackContents.at(nextStackZ * stackWidth + + (int)stackX).getEntryAlpha(y, oldNextVoxelHeightZ) && entryDest->hermiteZ != 0) { oldDistance = qAlpha(entryDest->hermiteZ) / (float)numeric_limits::max(); } if (flipped ? (spanner->intersects(pos + worldStepZ, pos, distance, normal) && From 8b2baf65e85d830352227b53c4a7eee84a32f4af Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 Jan 2015 14:13:23 -0800 Subject: [PATCH 37/67] Color/material fix. --- interface/src/MetavoxelSystem.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index ffa23f1b2d..1662f9a7ec 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1884,17 +1884,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g quint8 red = numeric_limits::max(); quint8 green = numeric_limits::max(); quint8 blue = numeric_limits::max(); + int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; if (colorSrc) { - const uchar* color = colorSrc + ((int)(clampedZ * colorStepZ) * colorWidth + - (int)(clampedX * colorStepX)) * DataBlock::COLOR_BYTES; + const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + + (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; red = color[0]; green = color[1]; blue = color[2]; } quint8 material = 0; if (materialSrc) { - material = materialSrc[(int)(clampedZ * materialStepZ) * materialWidth + - (int)(clampedX * materialStepX)]; + material = materialSrc[(int)(clampedOffsetZ * materialStepZ) * materialWidth + + (int)(clampedOffsetX * materialStepX)]; if (material != 0) { int& mapping = materialMap[material]; if (mapping == 0) { From 0dc4b26367d5481dfdbaf8feb16b55ee6c6d8e64 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 8 Jan 2015 21:37:53 -0800 Subject: [PATCH 38/67] Basic stack merging. --- libraries/metavoxels/src/Spanner.cpp | 123 +++++++++++++++++++++++++-- libraries/metavoxels/src/Spanner.h | 6 +- 2 files changed, 118 insertions(+), 11 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index d1bb7b6870..e53da2329c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2016,7 +2016,15 @@ static inline bool isSet(const QVector& stackContents, int stackWidt 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, float normalizeScale, float normalizeOffset) { - Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); + 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; + glm::vec3 expansion(1.0f / innerHeightWidth, 0.0f, 1.0f / innerHeightHeight); + Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(-expansion, scale + expansion); bool intersects = bounds.intersects(spanner->getBounds()); if (!intersects && normalizeScale == 1.0f && normalizeOffset == 0.0f) { return this; @@ -2041,13 +2049,6 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons } return newNode; } - 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; int highestHeightX = heightWidth - 1; int highestHeightZ = heightHeight - 1; QVector newHeightContents = _height->getContents(); @@ -2122,6 +2123,12 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons if (paint) { stepX = (float)innerHeightWidth / qMax(innerHeightWidth, qMax(innerColorWidth, innerMaterialWidth)); stepZ = (float)innerHeightHeight / qMax(innerHeightHeight, qMax(innerColorHeight, innerMaterialHeight)); + + } else { + start.x += 1.0f; + start.z += 1.0f; + end.x -= 1.0f; + end.z -= 1.0f; } float startX = glm::clamp(start.x, 0.0f, (float)highestHeightX), endX = glm::clamp(end.x, 0.0f, (float)highestHeightX); @@ -2651,6 +2658,8 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { int colorHeight = 0; int materialWidth = 0; int materialHeight = 0; + int stackWidth = 0; + int stackHeight = 0; for (int i = 0; i < CHILD_COUNT; i++) { HeightfieldHeightPointer childHeight = _children[i]->getHeight(); if (childHeight) { @@ -2673,6 +2682,13 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { materialWidth = qMax(materialWidth, childMaterialWidth); materialHeight = qMax(materialHeight, childMaterialHeight); } + HeightfieldStackPointer childStack = _children[i]->getStack(); + if (childStack) { + int childStackWidth = childStack->getWidth(); + int childStackHeight = childStack->getContents().size() / childStackWidth; + stackWidth = qMax(stackWidth, childStackWidth); + stackHeight = qMax(stackHeight, childStackHeight); + } } if (heightWidth > 0 && height) { QVector heightContents(heightWidth * heightHeight); @@ -2706,6 +2722,97 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { } else if (height) { _height.reset(); } + if (stackWidth > 0) { + QVector stackContents(stackWidth * stackHeight); + QVector stackMaterials; + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldStackPointer childStack = _children[i]->getStack(); + int childStackWidth = childStack->getWidth(); + int childStackHeight = childStack->getContents().size() / childStackWidth; + if (childStackWidth != stackWidth || childStackHeight != stackHeight) { + qWarning() << "Stack dimension mismatch [stackWidth=" << stackWidth << ", stackHeight=" << stackHeight << + ", childStackWidth=" << childStackWidth << ", childStackHeight=" << childStackHeight << "]"; + continue; + } + int innerStackWidth = stackWidth - HeightfieldData::SHARED_EDGE; + int innerStackHeight = stackHeight - HeightfieldData::SHARED_EDGE; + int innerQuadrantStackWidth = innerStackWidth / 2; + int innerQuadrantStackHeight = innerStackHeight / 2; + int quadrantStackWidth = innerQuadrantStackWidth + HeightfieldData::SHARED_EDGE; + int quadrantStackHeight = innerQuadrantStackHeight + HeightfieldData::SHARED_EDGE; + StackArray* dest = stackContents.data() + (i & Y_MAXIMUM_FLAG ? innerQuadrantStackHeight * stackWidth : 0) + + (i & X_MAXIMUM_FLAG ? innerQuadrantStackWidth : 0); + const StackArray* src = childStack->getContents().constData(); + QHash materialMap; + for (int z = 0; z < quadrantStackHeight; z++, dest += stackWidth, src += stackWidth * 2) { + const StackArray* lineSrc = src; + StackArray* lineDest = dest; + for (int x = 0; x < quadrantStackWidth; x++, lineDest++, lineSrc += 2) { + if (lineSrc->isEmpty()) { + continue; + } + int minimumY = lineSrc->getPosition(); + int maximumY = lineSrc->getPosition() + lineSrc->getEntryCount() - 1; + int newMinimumY = minimumY / 2; + int newMaximumY = maximumY / 2; + *lineDest = StackArray(newMaximumY - newMinimumY + 1); + lineDest->setPosition(newMinimumY); + int y = newMinimumY; + for (StackArray::Entry* destEntry = lineDest->getEntryData(), *end = destEntry + lineDest->getEntryCount(); + destEntry != end; destEntry++, y++) { + int srcY = y * 2; + const StackArray::Entry& srcEntry = lineSrc->getEntry(srcY); + destEntry->color = srcEntry.color; + destEntry->material = srcEntry.material; + if (destEntry->material != 0) { + int& mapping = materialMap[destEntry->material]; + if (mapping == 0) { + mapping = getMaterialIndex(childStack->getMaterials().at(destEntry->material - 1), + stackMaterials, stackContents); + } + destEntry->material = mapping; + } + int srcAlpha = qAlpha(srcEntry.color); + glm::vec3 normal; + if (srcAlpha != lineSrc->getEntryAlpha(srcY + 2)) { + const StackArray::Entry& nextSrcEntry = lineSrc->getEntry(srcY + 1); + if (qAlpha(nextSrcEntry.color) == srcAlpha) { + float distance = nextSrcEntry.getHermiteY(normal); + destEntry->setHermiteY(normal, distance * 0.5f + 0.5f); + } else { + float distance = srcEntry.getHermiteY(normal); + destEntry->setHermiteY(normal, distance * 0.5f); + } + } + if (x != quadrantStackWidth - 1 && srcAlpha != lineSrc[2].getEntryAlpha(srcY)) { + const StackArray::Entry& nextSrcEntry = lineSrc[1].getEntry(srcY); + if (qAlpha(nextSrcEntry.color) == srcAlpha) { + float distance = nextSrcEntry.getHermiteX(normal); + destEntry->setHermiteX(normal, distance * 0.5f + 0.5f); + } else { + float distance = srcEntry.getHermiteX(normal); + destEntry->setHermiteX(normal, distance * 0.5f); + } + } + if (z != quadrantStackHeight - 1 && srcAlpha != lineSrc[2 * stackWidth].getEntryAlpha(srcY)) { + const StackArray::Entry& nextSrcEntry = lineSrc[stackWidth].getEntry(srcY); + if (qAlpha(nextSrcEntry.color) == srcAlpha) { + float distance = nextSrcEntry.getHermiteZ(normal); + destEntry->setHermiteZ(normal, distance * 0.5f + 0.5f); + } else { + float distance = srcEntry.getHermiteZ(normal); + destEntry->setHermiteZ(normal, distance * 0.5f); + } + } + } + } + } + } + _stack = new HeightfieldStack(stackWidth, stackContents, stackMaterials); + + } else { + _stack.reset(); + } if (!colorMaterial) { return; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 224bd749a9..53dc40767a 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -577,10 +577,10 @@ public: Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); } const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); } - int getEntryAlpha(int y, float heightfieldHeight) const; + int getEntryAlpha(int y, float heightfieldHeight = 0.0f) const; - Entry& getEntry(int y, float heightfieldHeight); - const Entry& getEntry(int y, float heightfieldHeight) const; + Entry& getEntry(int y, float heightfieldHeight = 0.0f); + const Entry& getEntry(int y, float heightfieldHeight = 0.0f) const; void getExtents(int& minimumY, int& maximumY) const; From d82f2692634ecfc2ec6cf10eaa9fd3039f3d6eed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 Jan 2015 11:46:40 -0800 Subject: [PATCH 39/67] Don't show grid for tools where it isn't needed, started on "fill" tool. --- interface/src/ui/MetavoxelEditor.cpp | 25 ++++-- interface/src/ui/MetavoxelEditor.h | 19 +++- .../metavoxels/src/MetavoxelMessages.cpp | 18 ++++ libraries/metavoxels/src/MetavoxelMessages.h | 16 ++++ libraries/metavoxels/src/Spanner.cpp | 88 ++++++++++++++++++- libraries/metavoxels/src/Spanner.h | 9 ++ 6 files changed, 167 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 186aa7a48e..98ed64590e 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -129,6 +129,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); addTool(new HeightfieldSculptBrushTool(this)); + addTool(new HeightfieldFillBrushTool(this)); addTool(new HeightfieldMaterialBoxTool(this)); addTool(new HeightfieldMaterialSpannerTool(this)); @@ -330,6 +331,9 @@ void MetavoxelEditor::render() { MetavoxelTool* tool = getActiveTool(); if (tool) { tool->render(); + if (!tool->getUsesGrid()) { + return; + } } glDepthMask(GL_FALSE); @@ -385,10 +389,11 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const { ProgramObject MetavoxelEditor::_gridProgram; -MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : +MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing, bool usesGrid) : _editor(editor), _usesValue(usesValue), - _userFacing(userFacing) { + _userFacing(userFacing), + _usesGrid(usesGrid) { QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); @@ -669,7 +674,7 @@ void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const Share } RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Remove Spanner", false) { + MetavoxelTool(editor, "Remove Spanner", false, true, false) { } bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const { @@ -696,7 +701,7 @@ bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) { } ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Clear Spanners", false) { + MetavoxelTool(editor, "Clear Spanners", false, true, false) { QPushButton* button = new QPushButton("Clear"); layout()->addWidget(button); @@ -717,7 +722,7 @@ void ClearSpannersTool::clear() { } HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false) { + MetavoxelTool(editor, name, false, true, false) { QWidget* widget = new QWidget(); widget->setLayout(_form = new QFormLayout()); @@ -806,7 +811,7 @@ void ImportHeightfieldTool::updateSpanner() { } HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false), + MetavoxelTool(editor, name, false, true, false), _positionValid(false) { QWidget* widget = new QWidget(); @@ -968,6 +973,14 @@ QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) { } } +HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) : + HeightfieldBrushTool(editor, "Fill Brush") { +} + +QVariant HeightfieldFillBrushTool::createEdit(bool alternate) { + return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value())); +} + HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) : BoxTool(editor, "Set Material (Box)", false) { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index e3e2f38746..922cb298c4 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -92,12 +92,15 @@ class MetavoxelTool : public QWidget { public: - MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true); + MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, + bool userFacing = true, bool usesGrid = true); bool getUsesValue() const { return _usesValue; } bool isUserFacing() const { return _userFacing; } + bool getUsesGrid() const { return _usesGrid; } + virtual bool appliesTo(const AttributePointer& attribute) const; virtual void simulate(float deltaTime); @@ -113,6 +116,7 @@ protected: MetavoxelEditor* _editor; bool _usesValue; bool _userFacing; + bool _usesGrid; }; /// Base class for tools that allow dragging out a 3D box. @@ -404,6 +408,19 @@ private: MaterialControl* _materialControl; }; +/// Allows "filling" (removing dual contour stack data) parts of the heightfield. +class HeightfieldFillBrushTool : public HeightfieldBrushTool { + Q_OBJECT + +public: + + HeightfieldFillBrushTool(MetavoxelEditor* editor); + +protected: + + virtual QVariant createEdit(bool alternate); +}; + /// Allows setting heightfield materials by dragging out a box. class HeightfieldMaterialBoxTool : public BoxTool { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 50808156a7..5ba16ee754 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -201,3 +201,21 @@ void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakShared } } +FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius) : + position(position), + radius(radius) { +} + +void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + glm::vec3 extents = glm::vec3(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())->fillHeight(position, radius); + 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 4b941eaad2..0605fb4dd2 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -243,4 +243,20 @@ public: DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit) +/// An edit that sets a region of a heightfield height. +class FillHeightfieldHeightEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 position; + STREAM float radius; + + FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(FillHeightfieldHeightEdit) + #endif // hifi_MetavoxelMessages_h diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index e53da2329c..3840fda465 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -115,6 +115,10 @@ Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float hei return this; } +Spanner* Spanner::fillHeight(const glm::vec3& position, float radius) { + return this; +} + Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color, bool paint) { return this; @@ -1926,7 +1930,8 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons maybeRenormalize(scale, normalizeScale, normalizeOffset, innerStackWidth, newHeightContents, newStackContents); if (!intersects) { return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); + _color, _material, HeightfieldStackPointer(newStackContents.isEmpty() ? NULL : + new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } // now apply the actual change @@ -1956,6 +1961,80 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons lineDest += heightWidth; } + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), + _color, _material, HeightfieldStackPointer(newStackContents.isEmpty() ? NULL : + new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); +} + +HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius) { + 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; + int highestHeightX = heightWidth - 1; + int highestHeightZ = heightHeight - 1; + + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::vec3 center = glm::inverse(rotation) * (position - translation) * inverseScale + glm::vec3(1.0f, 0.0f, 1.0f); + glm::vec3 extents = radius * inverseScale; + + if (center.x + extents.x < 0.0f || center.z + extents.z < 0.0f || center.x - extents.x > highestHeightX || + center.z - extents.z > highestHeightZ) { + return this; + } + if (!isLeaf()) { + HeightfieldNode* newNode = this; + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); + 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); + if (_children[i] != newChild) { + if (newNode == this) { + newNode = new HeightfieldNode(*this); + } + newNode->setChild(i, HeightfieldNodePointer(newChild)); + } + } + if (newNode != this) { + newNode->mergeChildren(true, false); + } + return newNode; + } + if (!_stack) { + return this; + } + QVector newHeightContents = _height->getContents(); + + int stackWidth = _stack->getWidth(); + QVector newStackContents = _stack->getContents(); + QVector newStackMaterials = _stack->getMaterials(); + + glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), + glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + + quint16* lineDest = newHeightContents.data() + (int)start.z * heightWidth + (int)start.x; + float squaredRadius = extents.x * extents.x; + float multiplierZ = extents.x / extents.z; + for (float z = start.z; z <= end.z; z += 1.0f) { + quint16* dest = lineDest; + for (float x = start.x; x <= end.x; x += 1.0f, dest++) { + float dx = x - center.x, dz = (z - center.z) * multiplierZ; + float distanceSquared = dx * dx + dz * dz; + if (distanceSquared <= squaredRadius) { + + } + } + lineDest += heightWidth; + } + return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } @@ -3239,6 +3318,13 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float return newHeightfield; } +Spanner* Heightfield::fillHeight(const glm::vec3& position, float radius) { + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setRoot(HeightfieldNodePointer(_root->fillHeight(getTranslation(), getRotation(), + glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), position, radius))); + return newHeightfield; +} + Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color, bool paint) { // first see if we're going to exceed the range limits, normalizing if necessary diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 53dc40767a..3a7807dc1d 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -77,6 +77,10 @@ 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 fill the spanner's height (removing volumetric information). + /// \return the modified spanner, or this if no modification was performed + virtual Spanner* fillHeight(const glm::vec3& position, float radius); + /// Attempts to "sculpt" or "paint" with the supplied spanner. /// \return the modified spanner, or this if no modification was performed virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, @@ -691,6 +695,9 @@ public: HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius, float height, float normalizeScale, float normalizeOffset); + HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, + const glm::vec3& position, float radius); + void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& editBounds, float& minimum, float& maximum) const; @@ -795,6 +802,8 @@ public: virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height); + virtual Spanner* fillHeight(const glm::vec3& position, float radius); + virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, const QColor& color, bool paint); From 70890c68aa752c893f26b4b4f1c6ca8c81f2d9ae Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 Jan 2015 14:19:13 -0800 Subject: [PATCH 40/67] "Fill" brush (removes dual contour data). --- libraries/metavoxels/src/Spanner.cpp | 95 ++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 3840fda465..24a3ec9612 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2011,32 +2011,119 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const } QVector newHeightContents = _height->getContents(); - int stackWidth = _stack->getWidth(); QVector newStackContents = _stack->getContents(); + int stackWidth = _stack->getWidth(); + int stackHeight = newStackContents.size() / stackWidth; QVector newStackMaterials = _stack->getMaterials(); + int colorWidth, colorHeight; + QByteArray newColorContents; + if (_color) { + colorWidth = _color->getWidth(); + colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + newColorContents = _color->getContents(); + + } else { + colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE; + colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE; + newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF); + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + + int materialWidth, materialHeight; + QByteArray newMaterialContents; + QVector newMaterialMaterials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + newMaterialContents = _material->getContents(); + newMaterialMaterials = _material->getMaterials(); + + } else { + materialWidth = colorWidth; + materialHeight = colorHeight; + newMaterialContents = QByteArray(materialWidth * materialHeight, 0); + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + glm::vec3 start = glm::clamp(glm::floor(center - extents), glm::vec3(), glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); glm::vec3 end = glm::clamp(glm::ceil(center + extents), glm::vec3(), glm::vec3((float)highestHeightX, 0.0f, (float)highestHeightZ)); + float voxelStep = scale.x / innerHeightWidth; + float voxelScale = scale.y / (numeric_limits::max() * voxelStep); quint16* lineDest = newHeightContents.data() + (int)start.z * heightWidth + (int)start.x; float squaredRadius = extents.x * extents.x; float multiplierZ = extents.x / extents.z; + float colorStepX = (float)innerColorWidth / innerHeightWidth; + float colorStepZ = (float)innerColorHeight / innerHeightHeight; + float materialStepX = (float)innerMaterialWidth / innerHeightWidth; + float materialStepZ = (float)innerMaterialHeight / innerHeightHeight; + QHash materialMap; for (float z = start.z; z <= end.z; z += 1.0f) { quint16* dest = lineDest; for (float x = start.x; x <= end.x; x += 1.0f, dest++) { float dx = x - center.x, dz = (z - center.z) * multiplierZ; float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - + if (distanceSquared <= squaredRadius && x >= 1.0f && z >= 1.0f && x <= stackWidth && z <= stackHeight) { + int stackX = (int)x - 1, stackZ = (int)z - 1; + StackArray* stackDest = newStackContents.data() + stackZ * stackWidth + stackX; + if (stackDest->isEmpty()) { + continue; + } + int y = stackDest->getPosition() + stackDest->getEntryCount() - 1; + for (const StackArray::Entry* entry = stackDest->getEntryData() + stackDest->getEntryCount() - 1; + entry >= stackDest->getEntryData(); entry--, y--) { + if (!entry->isSet()) { + continue; + } + glm::vec3 normal; + int newHeight = qMax((int)((y + entry->getHermiteY(normal)) / voxelScale), 1); + if (newHeight < *dest) { + break; + } + *dest = newHeight; + for (int colorZ = stackZ * colorStepZ; (int)(colorZ / colorStepZ) == stackZ; colorZ++) { + for (int colorX = stackX * colorStepX; (int)(colorX / colorStepX) == stackX; colorX++) { + uchar* colorDest = (uchar*)newColorContents.data() + + (colorZ * colorWidth + colorX) * DataBlock::COLOR_BYTES; + colorDest[0] = qRed(entry->color); + colorDest[1] = qGreen(entry->color); + colorDest[2] = qBlue(entry->color); + } + } + for (int materialZ = stackZ * materialStepZ; (int)(materialZ / materialStepZ) == stackZ; materialZ++) { + for (int materialX = stackX * materialStepX; (int)(materialX / materialStepX) == stackX; + materialX++) { + int material = entry->material; + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + mapping = getMaterialIndex(newStackMaterials.at(material - 1), + newMaterialMaterials, newMaterialContents); + } + material = mapping; + } + newMaterialContents[materialZ * materialWidth + materialX] = material; + } + } + break; + } + stackDest->clear(); } } lineDest += heightWidth; } + clearUnusedMaterials(newMaterialMaterials, newMaterialContents); + clearUnusedMaterials(newStackMaterials, newStackContents); return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), - _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); + HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents)), + HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, newMaterialContents, newMaterialMaterials)), + HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, From dfbc3502fa1f61d7e5b6b6256847cac25ebdff46 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 Jan 2015 15:06:47 -0800 Subject: [PATCH 41/67] Use ray intersection code to get heightfield height. --- libraries/metavoxels/src/Spanner.cpp | 71 +++------------------------- libraries/metavoxels/src/Spanner.h | 2 - 2 files changed, 7 insertions(+), 66 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 24a3ec9612..bc1b993403 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1697,67 +1697,6 @@ bool HeightfieldNode::isLeaf() const { return true; } -float HeightfieldNode::getHeight(const glm::vec3& location) const { - if (location.x < 0.0f || location.z < 0.0f || location.x > 1.0f || location.z > 1.0f) { - return -FLT_MAX; - } - if (!isLeaf()) { - if (location.x < 0.5f) { - if (location.z < 0.5f) { - return _children[0]->getHeight(location * 2.0f); - } else { - return _children[Y_MAXIMUM_FLAG]->getHeight(location * 2.0f - glm::vec3(0.0f, 0.0f, 1.0f)); - } - } else { - if (location.z < 0.5f) { - return _children[X_MAXIMUM_FLAG]->getHeight(location * 2.0f - glm::vec3(1.0f, 0.0f, 0.0f)); - } else { - return _children[X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG]->getHeight(location * 2.0f - glm::vec3(1.0f, 0.0f, 1.0f)); - } - } - } - if (!_height) { - return -FLT_MAX; - } - int width = _height->getWidth(); - const QVector& contents = _height->getContents(); - const quint16* src = contents.constData(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - - glm::vec3 relative = location; - relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; - relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; - - // find the bounds of the cell containing the point and the shared vertex heights - glm::vec3 floors = glm::floor(relative); - glm::vec3 ceils = glm::ceil(relative); - glm::vec3 fracts = glm::fract(relative); - int floorX = (int)floors.x; - int floorZ = (int)floors.z; - int ceilX = (int)ceils.x; - int ceilZ = (int)ceils.z; - float upperLeft = src[floorZ * width + floorX]; - float lowerRight = src[ceilZ * width + ceilX]; - float interpolatedHeight = glm::mix(upperLeft, lowerRight, fracts.z); - - // the final vertex (and thus which triangle we check) depends on which half we're on - if (fracts.x >= fracts.z) { - float upperRight = src[floorZ * width + ceilX]; - interpolatedHeight = glm::mix(interpolatedHeight, glm::mix(upperRight, lowerRight, fracts.z), - (fracts.x - fracts.z) / (1.0f - fracts.z)); - - } else { - float lowerLeft = src[ceilZ * width + floorX]; - interpolatedHeight = glm::mix(glm::mix(upperLeft, lowerLeft, fracts.z), interpolatedHeight, fracts.x / fracts.z); - } - if (interpolatedHeight == 0.0f) { - return -FLT_MAX; // ignore zero values - } - return interpolatedHeight / numeric_limits::max(); -} - bool HeightfieldNode::findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, float& distance) const { glm::quat inverseRotation = glm::inverse(rotation); @@ -3380,9 +3319,13 @@ bool Heightfield::isHeightfield() const { } float Heightfield::getHeight(const glm::vec3& location) const { - float result = _root->getHeight(glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(), - 0.0f, 1.0f / (getScale() * _aspectZ))); - return (result == -FLT_MAX) ? -FLT_MAX : (getTranslation().y + result * getScale() * _aspectY); + float distance; + glm::vec3 down = getRotation() * glm::vec3(0.0f, -1.0f, 0.0f); + glm::vec3 origin = location - down * (glm::dot(down, location) + getScale() * _aspectY - glm::dot(down, getTranslation())); + if (findRayIntersection(origin, down, distance)) { + return origin.y + distance * down.y; + } + return -FLT_MAX; } bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 3a7807dc1d..5e4dae49bc 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -684,8 +684,6 @@ public: void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; } const HeightfieldNodePointer& getChild(int index) const { return _children[index]; } - float getHeight(const glm::vec3& location) const; - bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& origin, const glm::vec3& direction, float& distance) const; From d0023599892833c10e071c57db86b1659c5cf476 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 9 Jan 2015 16:43:51 -0800 Subject: [PATCH 42/67] Stitching improvement. --- interface/src/MetavoxelSystem.cpp | 48 ++++---- libraries/metavoxels/src/Spanner.cpp | 177 +++++++++++++-------------- 2 files changed, 110 insertions(+), 115 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 1662f9a7ec..6ba953bc4a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1847,34 +1847,32 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; if (stitch) { - // pack the corners set into a set of flags - int corners = 0; + glm::vec2 position; + int count = 0; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; if (qAlpha(crossing.color) == 0) { - int corner = (crossing.point.x == 1.0f) | ((crossing.point.z == 1.0f) << 1); - corners |= (1 << corner); + position += glm::vec2(crossing.point.x, crossing.point.z); + count++; } } - // fall into four cases based on set corners - int offsetX = 0, offsetZ = 0; - switch(corners) { - case 3: - case 11: // z- edge, x+/z- corner - offsetX = 1; - break; - - case 12: - case 13: // z+ edge, x-/z+ corner - offsetZ = 1; - break; - - case 10: - case 14: // x+ edge, x+/z+ corner - offsetX = offsetZ = 1; - break; + position /= count; + + const int CORNER_COUNT = 4; + float closestDistance = FLT_MAX; + int closestOffsetX = 0; + int closestOffsetZ = 0; + for (int i = 0; i < CORNER_COUNT; i++) { + int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; + float distance = glm::distance(position, glm::vec2(offsetX, offsetZ)); + if (distance < closestDistance && heightLineSrc[width * offsetZ + offsetX] != 0) { + closestDistance = distance; + closestOffsetX = offsetX; + closestOffsetZ = offsetZ; + } } - const quint16* height = heightLineSrc + width * offsetZ + offsetX; + const quint16* height = heightLineSrc + width * closestOffsetZ + closestOffsetX; int left = height[-1]; int right = height[1]; int down = height[-width]; @@ -1884,7 +1882,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g quint8 red = numeric_limits::max(); quint8 green = numeric_limits::max(); quint8 blue = numeric_limits::max(); - int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; + int clampedOffsetX = clampedX + closestOffsetX, clampedOffsetZ = clampedZ + closestOffsetZ; if (colorSrc) { const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; @@ -1905,8 +1903,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g material = mapping; } } - VoxelPoint point = { glm::vec3(clampedX + offsetX, *height * voxelScale, - clampedZ + offsetZ) * step, { red, green, blue }, + VoxelPoint point = { glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step, + { red, green, blue }, { (char)(normal.x * numeric_limits::max()), (char)(normal.y * numeric_limits::max()), (char)(normal.z * numeric_limits::max()) }, { material, 0, 0, 0 }, diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index bc1b993403..7ab10fbae1 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2827,6 +2827,93 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { } else if (height) { _height.reset(); } + if (colorWidth > 0 && colorMaterial) { + QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF); + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldColorPointer childColor = _children[i]->getColor(); + if (!childColor) { + continue; + } + int childColorWidth = childColor->getWidth(); + int childColorHeight = childColor->getContents().size() / (childColorWidth * DataBlock::COLOR_BYTES); + if (childColorWidth != colorWidth || childColorHeight != colorHeight) { + qWarning() << "Color dimension mismatch [colorWidth=" << colorWidth << ", colorHeight=" << colorHeight << + ", childColorWidth=" << childColorWidth << ", childColorHeight=" << childColorHeight << "]"; + continue; + } + int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; + int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; + int innerQuadrantColorWidth = innerColorWidth / 2; + int innerQuadrantColorHeight = innerColorHeight / 2; + int quadrantColorWidth = innerQuadrantColorWidth + HeightfieldData::SHARED_EDGE; + int quadrantColorHeight = innerQuadrantColorHeight + HeightfieldData::SHARED_EDGE; + char* dest = colorContents.data() + ((i & Y_MAXIMUM_FLAG ? innerQuadrantColorHeight * colorWidth : 0) + + (i & X_MAXIMUM_FLAG ? innerQuadrantColorWidth : 0)) * DataBlock::COLOR_BYTES; + const uchar* src = (const uchar*)childColor->getContents().constData(); + for (int z = 0; z < quadrantColorHeight; z++, dest += colorWidth * DataBlock::COLOR_BYTES, + src += colorWidth * DataBlock::COLOR_BYTES * 2) { + const uchar* lineSrc = src; + for (char* lineDest = dest, *end = dest + quadrantColorWidth * DataBlock::COLOR_BYTES; + lineDest != end; lineDest += DataBlock::COLOR_BYTES, lineSrc += DataBlock::COLOR_BYTES * 2) { + lineDest[0] = lineSrc[0]; + lineDest[1] = lineSrc[1]; + lineDest[2] = lineSrc[2]; + } + } + } + _color = new HeightfieldColor(colorWidth, colorContents); + + } else { + _color.reset(); + } + if (materialWidth > 0 && colorMaterial) { + QByteArray materialContents(materialWidth * materialHeight, 0); + QVector materials; + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldMaterialPointer childMaterial = _children[i]->getMaterial(); + if (!childMaterial) { + continue; + } + int childMaterialWidth = childMaterial->getWidth(); + int childMaterialHeight = childMaterial->getContents().size() / childMaterialWidth; + if (childMaterialWidth != materialWidth || childMaterialHeight != materialHeight) { + qWarning() << "Material dimension mismatch [materialWidth=" << materialWidth << ", materialHeight=" << + materialHeight << ", childMaterialWidth=" << childMaterialWidth << ", childMaterialHeight=" << + childMaterialHeight << "]"; + continue; + } + int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; + int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; + int innerQuadrantMaterialWidth = innerMaterialWidth / 2; + int innerQuadrantMaterialHeight = innerMaterialHeight / 2; + int quadrantMaterialWidth = innerQuadrantMaterialWidth + HeightfieldData::SHARED_EDGE; + int quadrantMaterialHeight = innerQuadrantMaterialHeight + HeightfieldData::SHARED_EDGE; + uchar* dest = (uchar*)materialContents.data() + + (i & Y_MAXIMUM_FLAG ? innerQuadrantMaterialHeight * materialWidth : 0) + + (i & X_MAXIMUM_FLAG ? innerQuadrantMaterialWidth : 0); + const uchar* src = (const uchar*)childMaterial->getContents().constData(); + QHash materialMap; + for (int z = 0; z < quadrantMaterialHeight; z++, dest += materialWidth, src += materialWidth * 2) { + const uchar* lineSrc = src; + for (uchar* lineDest = dest, *end = dest + quadrantMaterialWidth; lineDest != end; lineDest++, lineSrc += 2) { + int value = *lineSrc; + if (value != 0) { + int& mapping = materialMap[value]; + if (mapping == 0) { + mapping = getMaterialIndex(childMaterial->getMaterials().at(value - 1), + materials, materialContents); + } + value = mapping; + } + *lineDest = value; + } + } + } + _material = new HeightfieldMaterial(materialWidth, materialContents, materials); + + } else { + _material.reset(); + } if (stackWidth > 0) { QVector stackContents(stackWidth * stackHeight); QVector stackMaterials; @@ -2918,96 +3005,6 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { } else { _stack.reset(); } - if (!colorMaterial) { - return; - } - if (colorWidth > 0) { - QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF); - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldColorPointer childColor = _children[i]->getColor(); - if (!childColor) { - continue; - } - int childColorWidth = childColor->getWidth(); - int childColorHeight = childColor->getContents().size() / (childColorWidth * DataBlock::COLOR_BYTES); - if (childColorWidth != colorWidth || childColorHeight != colorHeight) { - qWarning() << "Color dimension mismatch [colorWidth=" << colorWidth << ", colorHeight=" << colorHeight << - ", childColorWidth=" << childColorWidth << ", childColorHeight=" << childColorHeight << "]"; - continue; - } - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - int innerQuadrantColorWidth = innerColorWidth / 2; - int innerQuadrantColorHeight = innerColorHeight / 2; - int quadrantColorWidth = innerQuadrantColorWidth + HeightfieldData::SHARED_EDGE; - int quadrantColorHeight = innerQuadrantColorHeight + HeightfieldData::SHARED_EDGE; - char* dest = colorContents.data() + ((i & Y_MAXIMUM_FLAG ? innerQuadrantColorHeight * colorWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantColorWidth : 0)) * DataBlock::COLOR_BYTES; - const uchar* src = (const uchar*)childColor->getContents().constData(); - for (int z = 0; z < quadrantColorHeight; z++, dest += colorWidth * DataBlock::COLOR_BYTES, - src += colorWidth * DataBlock::COLOR_BYTES * 2) { - const uchar* lineSrc = src; - for (char* lineDest = dest, *end = dest + quadrantColorWidth * DataBlock::COLOR_BYTES; - lineDest != end; lineDest += DataBlock::COLOR_BYTES, lineSrc += DataBlock::COLOR_BYTES * 2) { - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; - } - } - } - _color = new HeightfieldColor(colorWidth, colorContents); - - } else { - _color.reset(); - } - if (materialWidth > 0) { - QByteArray materialContents(materialWidth * materialHeight, 0); - QVector materials; - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldMaterialPointer childMaterial = _children[i]->getMaterial(); - if (!childMaterial) { - continue; - } - int childMaterialWidth = childMaterial->getWidth(); - int childMaterialHeight = childMaterial->getContents().size() / childMaterialWidth; - if (childMaterialWidth != materialWidth || childMaterialHeight != materialHeight) { - qWarning() << "Material dimension mismatch [materialWidth=" << materialWidth << ", materialHeight=" << - materialHeight << ", childMaterialWidth=" << childMaterialWidth << ", childMaterialHeight=" << - childMaterialHeight << "]"; - continue; - } - int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; - int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - int innerQuadrantMaterialWidth = innerMaterialWidth / 2; - int innerQuadrantMaterialHeight = innerMaterialHeight / 2; - int quadrantMaterialWidth = innerQuadrantMaterialWidth + HeightfieldData::SHARED_EDGE; - int quadrantMaterialHeight = innerQuadrantMaterialHeight + HeightfieldData::SHARED_EDGE; - uchar* dest = (uchar*)materialContents.data() + - (i & Y_MAXIMUM_FLAG ? innerQuadrantMaterialHeight * materialWidth : 0) + - (i & X_MAXIMUM_FLAG ? innerQuadrantMaterialWidth : 0); - const uchar* src = (const uchar*)childMaterial->getContents().constData(); - QHash materialMap; - for (int z = 0; z < quadrantMaterialHeight; z++, dest += materialWidth, src += materialWidth * 2) { - const uchar* lineSrc = src; - for (uchar* lineDest = dest, *end = dest + quadrantMaterialWidth; lineDest != end; lineDest++, lineSrc += 2) { - int value = *lineSrc; - if (value != 0) { - int& mapping = materialMap[value]; - if (mapping == 0) { - mapping = getMaterialIndex(childMaterial->getMaterials().at(value - 1), - materials, materialContents); - } - value = mapping; - } - *lineDest = value; - } - } - } - _material = new HeightfieldMaterial(materialWidth, materialContents, materials); - - } else { - _material.reset(); - } } QRgb HeightfieldNode::getColorAt(const glm::vec3& location) const { From 5f7a304c85c5eb62991a65d35e68971ce26c97f6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 Jan 2015 12:00:01 -0800 Subject: [PATCH 43/67] Crash fix. --- libraries/metavoxels/src/Spanner.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 7ab10fbae1..81eaf2d078 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2799,6 +2799,9 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { QVector heightContents(heightWidth * heightHeight); for (int i = 0; i < CHILD_COUNT; i++) { HeightfieldHeightPointer childHeight = _children[i]->getHeight(); + if (!childHeight) { + continue; + } int childHeightWidth = childHeight->getWidth(); int childHeightHeight = childHeight->getContents().size() / childHeightWidth; if (childHeightWidth != heightWidth || childHeightHeight != heightHeight) { @@ -2919,6 +2922,9 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { QVector stackMaterials; for (int i = 0; i < CHILD_COUNT; i++) { HeightfieldStackPointer childStack = _children[i]->getStack(); + if (!childStack) { + continue; + } int childStackWidth = childStack->getWidth(); int childStackHeight = childStack->getContents().size() / childStackWidth; if (childStackWidth != stackWidth || childStackHeight != stackHeight) { From f81c117ae5b15e15cad8e0998b485c84eedf1a65 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 15 Jan 2015 17:59:55 -0800 Subject: [PATCH 44/67] Stitching progress. --- interface/src/MetavoxelSystem.cpp | 290 ++++++++++++++++++++------- libraries/metavoxels/src/Spanner.cpp | 18 +- libraries/metavoxels/src/Spanner.h | 2 + 3 files changed, 235 insertions(+), 75 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 6ba953bc4a..589a84ff9a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1487,6 +1487,13 @@ const NormalIndex& IndexVector::get(int y) const { return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; } +static inline void appendIndices(QVector& indices, int i0, int i1, int i2, int i3) { + indices.append(i0); + indices.append(i1); + indices.append(i2); + indices.append(i3); +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -1695,9 +1702,89 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale; float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale; float nextHeightfieldHeightXZ = heightLineSrc[width + 1] * voxelScale; + const int UPPER_LEFT_CORNER = 1; + const int UPPER_RIGHT_CORNER = 2; + const int LOWER_LEFT_CORNER = 4; + const int LOWER_RIGHT_CORNER = 8; + const int NO_CORNERS = 0; + const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; + int corners = NO_CORNERS; + int stitchMinimumY = INT_MAX, stitchMaximumY = -1; + if (heightfieldHeight != 0.0f) { + corners |= UPPER_LEFT_CORNER; + stitchMinimumY = qMin(stitchMinimumY, (int)heightfieldHeight); + stitchMaximumY = qMax(stitchMaximumY, (int)heightfieldHeight); + } + if (nextHeightfieldHeightX != 0.0f) { + corners |= UPPER_RIGHT_CORNER; + stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightX); + stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightX); + } + if (nextHeightfieldHeightZ != 0.0f) { + corners |= LOWER_LEFT_CORNER; + stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightZ); + stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightZ); + } + if (nextHeightfieldHeightXZ != 0.0f) { + corners |= LOWER_RIGHT_CORNER; + stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightXZ); + stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightXZ); + } + bool stitchable = middleX && middleZ && !(corners == NO_CORNERS || corners == ALL_CORNERS); + bool stitched = false; + VoxelPoint cornerPoints[4]; + int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); + if (stitchable) { + for (unsigned int i = 0; i < sizeof(cornerPoints) / sizeof(cornerPoints[0]); i++) { + int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; + const quint16* height = heightLineSrc + offsetZ * width + offsetX; + if (*height == 0) { + continue; + } + VoxelPoint& point = cornerPoints[i]; + int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; + point.vertex = glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step; + int left = height[-1]; + int right = height[1]; + int down = height[-width]; + int up = height[width]; + glm::vec3 normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right, + 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up)); + point.normal[0] = normal.x * numeric_limits::max(); + point.normal[1] = normal.y * numeric_limits::max(); + point.normal[2] = normal.z * numeric_limits::max(); + if (colorSrc) { + const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + + (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; + point.color[0] = color[0]; + point.color[1] = color[1]; + point.color[2] = color[2]; + + } else { + point.color[0] = point.color[1] = point.color[2] = numeric_limits::max(); + } + int material = 0; + if (materialSrc) { + material = materialSrc[(int)(clampedOffsetZ * materialStepZ) * materialWidth + + (int)(clampedOffsetX * materialStepX)]; + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + mapping = getMaterialIndex(node->getMaterial()->getMaterials().at(material - 1), + stackMaterials); + } + material = mapping; + } + } + point.materials[0] = material; + point.materials[1] = point.materials[2] = point.materials[3] = 0; + point.materialWeights[0] = numeric_limits::max(); + point.materialWeights[1] = point.materialWeights[2] = point.materialWeights[3] = 0; + } + } for (int y = position, end = position + count; y < end; y++) { const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); - int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (displayHermite && x != 0 && z != 0) { glm::vec3 normal; if (entry.hermiteX != 0) { @@ -1845,73 +1932,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g break; } } - NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; - if (stitch) { - glm::vec2 position; - int count = 0; - for (int i = 0; i < crossingCount; i++) { - const EdgeCrossing& crossing = crossings[i]; - if (qAlpha(crossing.color) == 0) { - position += glm::vec2(crossing.point.x, crossing.point.z); - count++; - } - } - position /= count; - - const int CORNER_COUNT = 4; - float closestDistance = FLT_MAX; - int closestOffsetX = 0; - int closestOffsetZ = 0; - for (int i = 0; i < CORNER_COUNT; i++) { - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - float distance = glm::distance(position, glm::vec2(offsetX, offsetZ)); - if (distance < closestDistance && heightLineSrc[width * offsetZ + offsetX] != 0) { - closestDistance = distance; - closestOffsetX = offsetX; - closestOffsetZ = offsetZ; - } - } - const quint16* height = heightLineSrc + width * closestOffsetZ + closestOffsetX; - int left = height[-1]; - int right = height[1]; - int down = height[-width]; - int up = height[width]; - glm::vec3 normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right, - 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up)); - quint8 red = numeric_limits::max(); - quint8 green = numeric_limits::max(); - quint8 blue = numeric_limits::max(); - int clampedOffsetX = clampedX + closestOffsetX, clampedOffsetZ = clampedZ + closestOffsetZ; - if (colorSrc) { - const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + - (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; - red = color[0]; - green = color[1]; - blue = color[2]; - } - quint8 material = 0; - if (materialSrc) { - material = materialSrc[(int)(clampedOffsetZ * materialStepZ) * materialWidth + - (int)(clampedOffsetX * materialStepX)]; - if (material != 0) { - int& mapping = materialMap[material]; - if (mapping == 0) { - mapping = getMaterialIndex(node->getMaterial()->getMaterials().at(material - 1), - stackMaterials); - } - material = mapping; - } - } - VoxelPoint point = { glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step, - { red, green, blue }, - { (char)(normal.x * numeric_limits::max()), - (char)(normal.y * numeric_limits::max()), - (char)(normal.z * numeric_limits::max()) }, { material, 0, 0, 0 }, - { numeric_limits::max(), 0, 0, 0 } }; - vertices.append(point); - - } else { + NormalIndex index = { { -1, -1, -1, -1 } }; + if (!stitch) { + index.indices[0] = index.indices[1] = index.indices[2] = index.indices[3] = vertices.size(); glm::vec3 center; glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; int normalCount = 0; @@ -2044,17 +2067,138 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g point.setNormal(normals[i]); vertices.append(point); } + + if (stitchable && !stitched && y >= stitchMinimumY && y <= stitchMaximumY) { + int nextIndex = vertices.size(); + const NormalIndex& previousIndexX = lastIndicesX.get(y); + const NormalIndex& previousIndexZ = lastIndicesZ[x].get(y); + switch (corners) { + case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: { + vertices.append(cornerPoints[0]); + vertices.append(cornerPoints[3]); + glm::vec3 normal = glm::cross(cornerPoints[0].vertex - cornerPoints[1].vertex, + cornerPoints[3].vertex - cornerPoints[1].vertex); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + if (previousIndexX.isValid()) { + appendIndices(indices, firstIndex, firstIndex, nextIndex, + previousIndexX.getClosestIndex(normal, vertices)); + } + break; + } + case UPPER_LEFT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER: { + vertices.append(cornerPoints[0]); + vertices.append(cornerPoints[3]); + glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex, + cornerPoints[0].vertex - cornerPoints[2].vertex); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex, nextIndex + 1, nextIndex + 1); + if (previousIndexZ.isValid()) { + appendIndices(indices, firstIndex, firstIndex, + previousIndexZ.getClosestIndex(normal, vertices), nextIndex); + } + break; + } + case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: { + vertices.append(cornerPoints[1]); + vertices.append(cornerPoints[2]); + vertices.append(cornerPoints[3]); + glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex, + cornerPoints[1].vertex - cornerPoints[2].vertex); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 2, nextIndex, nextIndex); + appendIndices(indices,firstIndex, nextIndex + 1, nextIndex + 2, nextIndex + 2); + if (previousIndexX.isValid()) { + appendIndices(indices, firstIndex, firstIndex, + previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1); + } + if (previousIndexZ.isValid()) { + appendIndices(indices, firstIndex, firstIndex, nextIndex, + previousIndexZ.getClosestIndex(normal, vertices)); + } + break; + } + case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: { + vertices.append(cornerPoints[0]); + vertices.append(cornerPoints[1]); + vertices.append(cornerPoints[2]); + glm::vec3 normal = glm::cross(cornerPoints[2].vertex - cornerPoints[0].vertex, + cornerPoints[1].vertex - cornerPoints[0].vertex); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, firstIndex, nextIndex, nextIndex + 2, nextIndex + 2); + break; + } + case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: { + vertices.append(cornerPoints[0]); + vertices.append(cornerPoints[1]); + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(cornerPoints[1].vertex - first, + cornerPoints[0].vertex - first); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + if (previousIndexX.isValid()) { + appendIndices(indices, firstIndex, firstIndex, nextIndex, + previousIndexX.getClosestIndex(normal, vertices)); + } + break; + } + case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: { + vertices.append(cornerPoints[1]); + vertices.append(cornerPoints[3]); + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(cornerPoints[3].vertex - first, + cornerPoints[1].vertex - first); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + if (previousIndexZ.isValid()) { + appendIndices(indices, firstIndex, firstIndex, nextIndex, + previousIndexZ.getClosestIndex(normal, vertices)); + } + break; + } + case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: { + vertices.append(cornerPoints[3]); + vertices.append(cornerPoints[2]); + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(cornerPoints[2].vertex - first, + cornerPoints[3].vertex - first); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + if (previousIndexX.isValid()) { + appendIndices(indices, firstIndex, firstIndex, + previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1); + } + break; + } + case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: { + vertices.append(cornerPoints[2]); + vertices.append(cornerPoints[0]); + const glm::vec3& first = vertices.at(index.indices[0]).vertex; + glm::vec3 normal = glm::cross(cornerPoints[0].vertex - first, + cornerPoints[2].vertex - first); + int firstIndex = index.getClosestIndex(normal, vertices); + appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + if (previousIndexZ.isValid()) { + appendIndices(indices, firstIndex, firstIndex, + previousIndexZ.getClosestIndex(normal, vertices), nextIndex + 1); + } + break; + } + } + stitched = true; + } } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating // quads for each edge that includes a transition, using indices of previously generated vertices int reclampedX = qMin(clampedX, stackWidth - 1); int reclampedZ = qMin(clampedZ, stackHeight - 1); - if (alpha0 != alpha1) { + if (alpha0 != alpha1 && middleX) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); const NormalIndex& index3 = lastIndicesZ[x].get(y); - if (index1.isValid() && index2.isValid() && index3.isValid()) { + if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); if (reclampedZ > 0) { @@ -2082,7 +2226,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const NormalIndex& index1 = lastIndicesZ[x].get(y); const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); const NormalIndex& index3 = lastIndicesX.get(y); - if (index1.isValid() && index2.isValid() && index3.isValid()) { + if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); if (reclampedX > 0) { quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); @@ -2110,11 +2254,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } - if (alpha0 != alpha4) { + if (alpha0 != alpha4 && middleZ) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesX.get(y - 1); const NormalIndex& index3 = lastIndicesX.get(y); - if (index1.isValid() && index2.isValid() && index3.isValid()) { + if (index.isValid() && index1.isValid() && index2.isValid() && index3.isValid()) { quadIndices.insert(qRgb(reclampedX, y, reclampedZ), indices.size()); if (reclampedX > 0) { quadIndices.insert(qRgb(reclampedX - 1, y, reclampedZ), indices.size()); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 81eaf2d078..636dc27cba 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1389,6 +1389,18 @@ void StackArray::getExtents(int& minimumY, int& maximumY) const { } } +bool StackArray::hasSetEntries() const { + int count = getEntryCount(); + if (count > 0) { + for (const Entry* entry = getEntryData(), *end = entry + count; entry != end; entry++) { + if (entry->isSet()) { + return true; + } + } + } + return false; +} + HeightfieldStack::HeightfieldStack(int width, const QVector& contents, const QVector& materials) : HeightfieldData(width), @@ -2368,7 +2380,9 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest->material = index; } if (y + 1 > voxelHeight) { - *heightLineDest = 0; + if (x < startX && z < startZ && x > endX && z > endZ) { + *heightLineDest = 0; + } entryDest->setHermiteY(glm::normalize(glm::vec3(deltaX, 2.0f, deltaZ)), voxelHeight - y); } } @@ -2382,7 +2396,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float oldVoxelHeight = *oldHeightLineDest * voxelScale; float oldNextVoxelHeightX = oldHeightLineDest[1] * voxelScale; float oldNextVoxelHeightZ = oldHeightLineDest[heightWidth] * voxelScale; - for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { + for (int y = newTop; y >= newBottom && false; y--, entryDest--, pos -= worldStepY) { int oldCurrentAlpha = stackDest->getEntryAlpha(y, oldVoxelHeight); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 5e4dae49bc..6a445ad84e 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -588,6 +588,8 @@ public: void getExtents(int& minimumY, int& maximumY) const; + bool hasSetEntries() const; + void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); } }; From 45b36b383c65851e08a1aa0be2b06264dc60351a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 16 Jan 2015 14:32:15 -0800 Subject: [PATCH 45/67] More progress on stitching, etc. --- interface/src/Menu.cpp | 3 +- interface/src/MetavoxelSystem.cpp | 132 ++++++++++++++++++--------- interface/src/MetavoxelSystem.h | 4 - libraries/metavoxels/src/Spanner.cpp | 17 ++-- 4 files changed, 99 insertions(+), 57 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index aa2b7878d3..57134bb84b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -419,8 +419,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); - addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, - Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); + addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true); addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index ba4ea16752..b0c3dafbb9 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -515,18 +515,6 @@ void MetavoxelSystem::render() { emit rendering(); } -void MetavoxelSystem::refreshVoxelData() { - DependencyManager::get()->eachNode([](const SharedNodePointer& node){ - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelSystemClient* client = static_cast(node->getLinkedData()); - if (client) { - QMetaObject::invokeMethod(client, "refreshVoxelData"); - } - } - }); -} - void MetavoxelSystem::paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color) { Sphere* sphere = new Sphere(); sphere->setTranslation(position); @@ -1479,6 +1467,7 @@ public: void swap(IndexVector& other) { QVector::swap(other); qSwap(position, other.position); } const NormalIndex& get(int y) const; + const NormalIndex& getClosest(int y) const; }; const NormalIndex& IndexVector::get(int y) const { @@ -1487,11 +1476,54 @@ const NormalIndex& IndexVector::get(int y) const { return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; } -static inline void appendIndices(QVector& indices, int i0, int i1, int i2, int i3) { - indices.append(i0); - indices.append(i1); - indices.append(i2); - indices.append(i3); +const NormalIndex& IndexVector::getClosest(int y) const { + static NormalIndex invalidIndex = { { -1, -1, -1, -1 } }; + int relative = y - position; + if (relative < 0 || relative >= size()) { + return invalidIndex; + } + const NormalIndex& first = at(relative); + if (first.isValid()) { + return first; + } + for (int distance = 1; relative - distance >= 0 || relative + distance < size(); distance++) { + int previous = relative - distance; + if (previous >= 0) { + const NormalIndex& previousIndex = at(previous); + if (previousIndex.isValid()) { + return previousIndex; + } + } + int next = relative + distance; + if (next < size()) { + const NormalIndex& nextIndex = at(next); + if (nextIndex.isValid()) { + return nextIndex; + } + } + } + return invalidIndex; +} + +static inline void appendIndices(QVector& indices, QMultiHash& quadIndices, + const QVector& vertices, float step, int i0, int i1, int i2, int i3) { + int newIndices[] = { i0, i1, i2, i3 }; + glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); + int indexIndex = indices.size(); + for (unsigned int i = 0; i < sizeof(newIndices) / sizeof(newIndices[0]); i++) { + int index = newIndices[i]; + indices.append(index); + const glm::vec3& vertex = vertices.at(index).vertex; + minima = glm::min(vertex, minima); + maxima = glm::max(vertex, maxima); + } + for (int z = (int)minima.z, endZ = (int)glm::ceil(maxima.z); z < endZ; z++) { + for (int y = (int)minima.x, endY = (int)glm::ceil(maxima.y); y < endY; y++) { + for (int x = (int)minima.x, endX = (int)glm::ceil(maxima.x); x < endX; x++) { + quadIndices.insert(qRgb(x, y, z), indexIndex); + } + } + } } void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, @@ -1924,16 +1956,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ); } } - // determine whether we need to stitch into surrounding heightfield - bool stitch = false; + // determine whether we should ignore this vertex because it will be stitched + bool ignore = false; for (int i = 0; i < crossingCount; i++) { if (qAlpha(crossings[i].color) == 0) { - stitch = true; + ignore = true; break; } } NormalIndex index = { { -1, -1, -1, -1 } }; - if (!stitch) { + if (!ignore) { index.indices[0] = index.indices[1] = index.indices[2] = index.indices[3] = vertices.size(); glm::vec3 center; glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; @@ -2070,8 +2102,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (stitchable && !stitched && y >= stitchMinimumY && y <= stitchMaximumY) { int nextIndex = vertices.size(); - const NormalIndex& previousIndexX = lastIndicesX.get(y); - const NormalIndex& previousIndexZ = lastIndicesZ[x].get(y); + const NormalIndex& previousIndexX = lastIndicesX.getClosest(y); + const NormalIndex& previousIndexZ = lastIndicesZ[x].getClosest(y); switch (corners) { case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: { vertices.append(cornerPoints[0]); @@ -2079,10 +2111,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[0].vertex - cornerPoints[1].vertex, cornerPoints[3].vertex - cornerPoints[1].vertex); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); if (previousIndexX.isValid()) { - appendIndices(indices, firstIndex, firstIndex, nextIndex, - previousIndexX.getClosestIndex(normal, vertices)); + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + nextIndex, previousIndexX.getClosestIndex(normal, vertices)); } break; } @@ -2092,9 +2125,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex, cornerPoints[0].vertex - cornerPoints[2].vertex); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex, nextIndex + 1, nextIndex + 1); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex, nextIndex + 1, nextIndex + 1); if (previousIndexZ.isValid()) { - appendIndices(indices, firstIndex, firstIndex, + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, previousIndexZ.getClosestIndex(normal, vertices), nextIndex); } break; @@ -2106,15 +2140,17 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex, cornerPoints[1].vertex - cornerPoints[2].vertex); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 2, nextIndex, nextIndex); - appendIndices(indices,firstIndex, nextIndex + 1, nextIndex + 2, nextIndex + 2); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 2, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex + 2, nextIndex + 2); if (previousIndexX.isValid()) { - appendIndices(indices, firstIndex, firstIndex, + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1); } if (previousIndexZ.isValid()) { - appendIndices(indices, firstIndex, firstIndex, nextIndex, - previousIndexZ.getClosestIndex(normal, vertices)); + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + nextIndex, previousIndexZ.getClosestIndex(normal, vertices)); } break; } @@ -2125,8 +2161,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[2].vertex - cornerPoints[0].vertex, cornerPoints[1].vertex - cornerPoints[0].vertex); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); - appendIndices(indices, firstIndex, nextIndex, nextIndex + 2, nextIndex + 2); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex, nextIndex + 2, nextIndex + 2); break; } case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: { @@ -2136,10 +2174,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[1].vertex - first, cornerPoints[0].vertex - first); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); if (previousIndexX.isValid()) { - appendIndices(indices, firstIndex, firstIndex, nextIndex, - previousIndexX.getClosestIndex(normal, vertices)); + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + nextIndex, previousIndexX.getClosestIndex(normal, vertices)); } break; } @@ -2150,10 +2189,11 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[3].vertex - first, cornerPoints[1].vertex - first); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); if (previousIndexZ.isValid()) { - appendIndices(indices, firstIndex, firstIndex, nextIndex, - previousIndexZ.getClosestIndex(normal, vertices)); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + firstIndex, nextIndex, previousIndexZ.getClosestIndex(normal, vertices)); } break; } @@ -2164,9 +2204,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[2].vertex - first, cornerPoints[3].vertex - first); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); if (previousIndexX.isValid()) { - appendIndices(indices, firstIndex, firstIndex, + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1); } break; @@ -2178,9 +2219,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec3 normal = glm::cross(cornerPoints[0].vertex - first, cornerPoints[2].vertex - first); int firstIndex = index.getClosestIndex(normal, vertices); - appendIndices(indices, firstIndex, nextIndex + 1, nextIndex, nextIndex); + appendIndices(indices, quadIndices, vertices, step, firstIndex, + nextIndex + 1, nextIndex, nextIndex); if (previousIndexZ.isValid()) { - appendIndices(indices, firstIndex, firstIndex, + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, previousIndexZ.getClosestIndex(normal, vertices), nextIndex + 1); } break; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 98c7ff9965..15277e8a84 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -87,10 +87,6 @@ signals: void rendering(); -public slots: - - void refreshVoxelData(); - protected: Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 636dc27cba..0a7424b2eb 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2193,6 +2193,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons return new HeightfieldNode(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents)), _color, _material, HeightfieldStackPointer(new HeightfieldStack(stackWidth, newStackContents, newStackMaterials))); } + QVector oldHeightContents = newHeightContents; QVector oldStackContents = newStackContents; int colorWidth, colorHeight; @@ -2285,10 +2286,14 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons (int)materialZ * materialWidth + (int)materialX) : NULL; if (paint && *heightLineDest != 0 && spanner->contains(worldPos + worldStepY * (*heightLineDest * voxelScale))) { - colorDest[0] = qRed(rgba); - colorDest[1] = qGreen(rgba); - colorDest[2] = qBlue(rgba); - *materialDest = materialMaterialIndex; + if (colorDest) { + colorDest[0] = qRed(rgba); + colorDest[1] = qGreen(rgba); + colorDest[2] = qBlue(rgba); + } + if (materialDest) { + *materialDest = materialMaterialIndex; + } } float stackX = (x - HeightfieldHeight::HEIGHT_BORDER) * innerStackWidth / innerHeightWidth; @@ -2333,7 +2338,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons stackDest->setPosition(newBottom); prepend = stackDest->getEntryCount(); } - const quint16* oldHeightLineDest = _height->getContents().constData() + (int)z * heightWidth + (int)x; + const quint16* oldHeightLineDest = oldHeightContents.constData() + (int)z * heightWidth + (int)x; if (*heightLineDest != 0) { float voxelHeight = *heightLineDest * voxelScale; float left = oldHeightLineDest[-1] * voxelScale; @@ -2396,7 +2401,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float oldVoxelHeight = *oldHeightLineDest * voxelScale; float oldNextVoxelHeightX = oldHeightLineDest[1] * voxelScale; float oldNextVoxelHeightZ = oldHeightLineDest[heightWidth] * voxelScale; - for (int y = newTop; y >= newBottom && false; y--, entryDest--, pos -= worldStepY) { + for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { int oldCurrentAlpha = stackDest->getEntryAlpha(y, oldVoxelHeight); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { From 2497b2fe39debd321d97fa3eed03d0db107ee3f3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 16 Jan 2015 15:41:08 -0800 Subject: [PATCH 46/67] If there's no directly intersecting spanner, find the closest heightfield that we can project onto. --- interface/src/MetavoxelSystem.cpp | 21 +++++--- .../metavoxels/src/MetavoxelMessages.cpp | 52 +++++++++++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b0c3dafbb9..f3c3c94255 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1957,15 +1957,14 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } // determine whether we should ignore this vertex because it will be stitched - bool ignore = false; + int validCrossings = 0; for (int i = 0; i < crossingCount; i++) { - if (qAlpha(crossings[i].color) == 0) { - ignore = true; - break; + if (qAlpha(crossings[i].color) != 0) { + validCrossings++; } } NormalIndex index = { { -1, -1, -1, -1 } }; - if (!ignore) { + if (validCrossings != 0) { index.indices[0] = index.indices[1] = index.indices[2] = index.indices[3] = vertices.size(); glm::vec3 center; glm::vec3 normals[MAX_NORMALS_PER_VERTEX]; @@ -1978,6 +1977,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g int red = 0, green = 0, blue = 0; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; + if (qAlpha(crossing.color) == 0) { + continue; + } center += crossing.point; int j = 0; @@ -2013,7 +2015,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } } - center /= crossingCount; + center /= validCrossings; // use a sequence of Givens rotations to perform a QR decomposition // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf @@ -2021,6 +2023,9 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g glm::vec4 bottom; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; + if (qAlpha(crossing.color) == 0) { + continue; + } bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); for (int j = 0; j < 4; j++) { @@ -2085,8 +2090,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g materialWeights *= (numeric_limits::max() / totalWeight); } VoxelPoint point = { (glm::vec3(clampedX, y, clampedZ) + center) * step, - { (quint8)(red / crossingCount), (quint8)(green / crossingCount), - (quint8)(blue / crossingCount) }, + { (quint8)(red / validCrossings), (quint8)(green / validCrossings), + (quint8)(blue / validCrossings) }, { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 5ba16ee754..cfe97a73af 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "MetavoxelMessages.h" #include "Spanner.h" @@ -180,6 +182,50 @@ HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjec paint(paint) { } +class SpannerProjectionFetchVisitor : public SpannerVisitor { +public: + + SpannerProjectionFetchVisitor(const Box& bounds, QVector& results); + + virtual bool visit(Spanner* spanner); + +private: + + const Box& _bounds; + QVector& _results; + float _closestDistance; +}; + +SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector& results) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), + _bounds(bounds), + _results(results), + _closestDistance(FLT_MAX) { +} + +bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) { + Heightfield* heightfield = qobject_cast(spanner); + if (!heightfield) { + return true; + } + glm::mat4 transform = glm::scale(1.0f / glm::vec3(heightfield->getScale(), + heightfield->getScale() * heightfield->getAspectY(), + heightfield->getScale() * heightfield->getAspectZ())) * + glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation()); + Box transformedBounds = transform * _bounds; + if (transformedBounds.maximum.x < 0.0f && transformedBounds.maximum.z < 0.0f && + transformedBounds.minimum.x > 1.0f && transformedBounds.minimum.z > 1.0f) { + return true; + } + float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y)); + if (distance < _closestDistance) { + _results.clear(); + _results.append(spanner); + _closestDistance = distance; + } + return true; +} + void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // make sure the color meets our transparency requirements QColor color = averageColor; @@ -193,6 +239,12 @@ void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakShared data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), static_cast(spanner.data())->getBounds(), results); + // if there's nothing intersecting directly, find the closest heightfield that intersects the projection + if (results.isEmpty()) { + SpannerProjectionFetchVisitor visitor(static_cast(spanner.data())->getBounds(), results); + data.guide(visitor); + } + foreach (const SharedObjectPointer& result, results) { Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color, paint); if (newResult != result) { From 3dbbbb72b09941af2fd4fb6cafa4920248f046a9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 16 Jan 2015 16:49:22 -0800 Subject: [PATCH 47/67] Fixes for deltas when root hasn't changed, range updates. --- .../metavoxels/src/MetavoxelMessages.cpp | 4 +-- libraries/metavoxels/src/Spanner.cpp | 29 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index cfe97a73af..a5bcd5fc40 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -213,8 +213,8 @@ bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) { heightfield->getScale() * heightfield->getAspectZ())) * glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation()); Box transformedBounds = transform * _bounds; - if (transformedBounds.maximum.x < 0.0f && transformedBounds.maximum.z < 0.0f && - transformedBounds.minimum.x > 1.0f && transformedBounds.minimum.z > 1.0f) { + if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f || + transformedBounds.minimum.x > 1.0f || transformedBounds.minimum.z > 1.0f) { return true; } float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y)); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 0a7424b2eb..1937dd5934 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2079,8 +2079,19 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const Box& editBounds, float& minimum, float& maximum) const { - Box bounds = glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale); - if (!bounds.intersects(editBounds)) { + if (!_height) { + return; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); + glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); + glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; + Box transformedBounds = inverseTransform * editBounds; + if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f || + transformedBounds.minimum.x > heightWidth - 1 || transformedBounds.minimum.z > heightHeight - 1) { return; } if (!isLeaf()) { @@ -2093,19 +2104,6 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: } return; } - if (!_height) { - return; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - - glm::mat4 baseInverseTransform = glm::mat4_cast(glm::inverse(rotation)) * glm::translate(-translation); - glm::vec3 inverseScale(innerHeightWidth / scale.x, numeric_limits::max() / scale.y, innerHeightHeight / scale.z); - glm::mat4 inverseTransform = glm::translate(glm::vec3(1.0f, 0.0f, 1.0f)) * glm::scale(inverseScale) * baseInverseTransform; - Box transformedBounds = inverseTransform * editBounds; - glm::vec3 start = glm::floor(transformedBounds.minimum); glm::vec3 end = glm::ceil(transformedBounds.maximum); @@ -3764,6 +3762,7 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { HeightfieldStreamBase base = { in, lod, referenceLOD }; HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + setRoot(static_cast(reference)->getRoot()); bool changed; in >> changed; if (changed) { From debc9b735deed3ab7aba8662bf91c5ca0963dd05 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 17 Jan 2015 16:33:35 -0800 Subject: [PATCH 48/67] Fixed edge issue, increased default grid size to correspond to default heightfield import. --- interface/src/MetavoxelSystem.cpp | 6 +++--- interface/src/ui/MetavoxelEditor.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index f3c3c94255..268f63e5c5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2241,7 +2241,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // quads for each edge that includes a transition, using indices of previously generated vertices int reclampedX = qMin(clampedX, stackWidth - 1); int reclampedZ = qMin(clampedZ, stackHeight - 1); - if (alpha0 != alpha1 && middleX) { + if (alpha0 != alpha1 && y > position && z > 0) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesZ[x].get(y - 1); const NormalIndex& index3 = lastIndicesZ[x].get(y); @@ -2269,7 +2269,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } - if (alpha0 != alpha2) { + if (alpha0 != alpha2 && x > 0 && z > 0) { const NormalIndex& index1 = lastIndicesZ[x].get(y); const NormalIndex& index2 = lastIndicesZ[x - 1].get(y); const NormalIndex& index3 = lastIndicesX.get(y); @@ -2301,7 +2301,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } } - if (alpha0 != alpha4 && middleZ) { + if (alpha0 != alpha4 && x > 0 && y > position) { const NormalIndex& index1 = lastIndexY; const NormalIndex& index2 = lastIndicesX.get(y - 1); const NormalIndex& index3 = lastIndicesX.get(y); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 98ed64590e..bb05d054d8 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -97,7 +97,7 @@ MetavoxelEditor::MetavoxelEditor() : _gridSpacing->setMinimum(-FLT_MAX); _gridSpacing->setMaximum(FLT_MAX); _gridSpacing->setPrefix("2^"); - _gridSpacing->setValue(-3.0); + _gridSpacing->setValue(0.0); connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition())); formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); From 5bb441b6efae6d605ee497f904b6aecc7e68a355 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sat, 17 Jan 2015 20:17:28 -0800 Subject: [PATCH 49/67] Added a "voxelize" brush for debugging. --- interface/src/ui/MetavoxelEditor.cpp | 15 +++++++++++++-- interface/src/ui/MetavoxelEditor.h | 4 ++++ libraries/metavoxels/src/MetavoxelMessages.cpp | 7 ++++--- libraries/metavoxels/src/MetavoxelMessages.h | 7 ++++--- libraries/metavoxels/src/Spanner.cpp | 15 ++++++++------- libraries/metavoxels/src/Spanner.h | 12 ++++++------ 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index bb05d054d8..6bd1ea2438 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -821,7 +821,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin _form->addRow("Radius:", _radius = new QDoubleSpinBox()); _radius->setSingleStep(0.01); _radius->setMaximum(FLT_MAX); - _radius->setValue(1.0); + _radius->setValue(5.0); } bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const { @@ -975,10 +975,21 @@ QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) { HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) : HeightfieldBrushTool(editor, "Fill Brush") { + + _form->addRow("Mode:", _mode = new QComboBox()); + _mode->addItem("Fill"); + _mode->addItem("Voxelize"); } QVariant HeightfieldFillBrushTool::createEdit(bool alternate) { - return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value())); + if (_mode->currentIndex() == 0) { + return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value())); + } + Sphere* sphere = new Sphere(); + sphere->setTranslation(_position); + sphere->setScale(_radius->value()); + return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere), + SharedObjectPointer(), QColor(), false, true)); } HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) : diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 922cb298c4..5ddf582f52 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -419,6 +419,10 @@ public: protected: virtual QVariant createEdit(bool alternate); + +private: + + QComboBox* _mode; }; /// Allows setting heightfield materials by dragging out a box. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index a5bcd5fc40..f95efeb359 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -176,10 +176,11 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av } HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner, - const SharedObjectPointer& material, const QColor& averageColor, bool paint) : + const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize) : MaterialEdit(material, averageColor), spanner(spanner), - paint(paint) { + paint(paint), + voxelize(voxelize) { } class SpannerProjectionFetchVisitor : public SpannerVisitor { @@ -246,7 +247,7 @@ void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakShared } foreach (const SharedObjectPointer& result, results) { - Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color, paint); + Spanner* newResult = static_cast(result.data())->setMaterial(spanner, material, color, paint, voxelize); if (newResult != result) { data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 0605fb4dd2..4e478e5109 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -233,17 +233,18 @@ public: STREAM SharedObjectPointer spanner; STREAM bool paint; + STREAM bool voxelize; HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(), const SharedObjectPointer& material = SharedObjectPointer(), - const QColor& averageColor = QColor(), bool paint = false); + const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit) -/// An edit that sets a region of a heightfield height. +/// An edit that fills a region of a heightfield height. class FillHeightfieldHeightEdit : public MetavoxelEdit { STREAMABLE @@ -251,7 +252,7 @@ public: STREAM glm::vec3 position; STREAM float radius; - + FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 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 1937dd5934..f9e650f4ca 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -120,7 +120,7 @@ Spanner* Spanner::fillHeight(const glm::vec3& position, float radius) { } Spanner* Spanner::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint) { + const QColor& color, bool paint, bool voxelize) { return this; } @@ -2129,7 +2129,7 @@ static inline bool isSet(const QVector& stackContents, int stackWidt } 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, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, float normalizeScale, float normalizeOffset) { if (!_height) { return this; @@ -2150,8 +2150,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons glm::vec3 nextScale = scale * glm::vec3(0.5f, 1.0f, 0.5f); 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, normalizeScale, normalizeOffset); + i & Y_MAXIMUM_FLAG ? nextScale.z : 0.0f), rotation, nextScale, spanner, + material, color, paint, voxelize, normalizeScale, normalizeOffset); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -2399,7 +2399,8 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons float oldVoxelHeight = *oldHeightLineDest * voxelScale; float oldNextVoxelHeightX = oldHeightLineDest[1] * voxelScale; float oldNextVoxelHeightZ = oldHeightLineDest[heightWidth] * voxelScale; - for (int y = newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { + // skip the actual set if voxelizing + for (int y = voxelize ? newBottom - 1 : newTop; y >= newBottom; y--, entryDest--, pos -= worldStepY) { int oldCurrentAlpha = stackDest->getEntryAlpha(y, oldVoxelHeight); if (spanner->contains(pos)) { if (hasOwnColors && !erase) { @@ -3376,7 +3377,7 @@ Spanner* Heightfield::fillHeight(const glm::vec3& position, float radius) { } Spanner* Heightfield::setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint) { + const QColor& color, bool paint, bool voxelize) { // 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; @@ -3391,7 +3392,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, normalizeScale, normalizeOffset))); + material, color, paint, voxelize, normalizeScale, normalizeOffset))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6a445ad84e..48daf380fd 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -77,14 +77,14 @@ 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 fill the spanner's height (removing volumetric information). + /// 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); - /// Attempts to "sculpt" or "paint" with the supplied spanner. + /// 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); + const QColor& color, bool paint, bool voxelize); /// Checks whether this spanner has its own colors. virtual bool hasOwnColors() const; @@ -700,9 +700,9 @@ public: 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, + Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize, float normalizeScale, float normalizeOffset); void read(HeightfieldStreamState& state); @@ -805,7 +805,7 @@ public: virtual Spanner* fillHeight(const glm::vec3& position, float radius); virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material, - const QColor& color, bool paint); + const QColor& color, bool paint, bool voxelize); virtual bool hasOwnColors() const; virtual bool hasOwnMaterials() const; From 61cbafadd4a83f7c26151e4bd5f7293b72efbe9b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 00:18:35 -0800 Subject: [PATCH 50/67] More corner cases. --- interface/src/MetavoxelSystem.cpp | 37 ++++++++++++++++++++++++++++ libraries/metavoxels/src/Spanner.cpp | 4 +-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 268f63e5c5..0440c7f79a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2232,6 +2232,43 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } break; } + case UPPER_LEFT_CORNER: { + vertices.append(cornerPoints[0]); + glm::vec3 normal = glm::cross(cornerPoints[0].vertex - + vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f)); + int firstIndex = index.getClosestIndex(normal, vertices); + if (previousIndexX.isValid()) { + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + nextIndex, previousIndexX.getClosestIndex(normal, vertices)); + } + if (previousIndexZ.isValid()) { + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + previousIndexZ.getClosestIndex(normal, vertices), nextIndex); + } + break; + } + case UPPER_RIGHT_CORNER: { + vertices.append(cornerPoints[1]); + glm::vec3 normal = glm::cross(cornerPoints[1].vertex - + vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f)); + int firstIndex = index.getClosestIndex(normal, vertices); + if (previousIndexZ.isValid()) { + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + nextIndex, previousIndexZ.getClosestIndex(normal, vertices)); + } + break; + } + case LOWER_LEFT_CORNER: { + vertices.append(cornerPoints[2]); + glm::vec3 normal = glm::cross(cornerPoints[2].vertex - + vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f)); + int firstIndex = index.getClosestIndex(normal, vertices); + if (previousIndexX.isValid()) { + appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex, + previousIndexX.getClosestIndex(normal, vertices), nextIndex); + } + break; + } } stitched = true; } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f9e650f4ca..be030b6bc4 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2383,9 +2383,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons entryDest->material = index; } if (y + 1 > voxelHeight) { - if (x < startX && z < startZ && x > endX && z > endZ) { - *heightLineDest = 0; - } + *heightLineDest = 0; entryDest->setHermiteY(glm::normalize(glm::vec3(deltaX, 2.0f, deltaZ)), voxelHeight - y); } } From 393d537c16728c13e0576278193b0488e1afef5e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 00:26:43 -0800 Subject: [PATCH 51/67] Edge fix. --- interface/src/MetavoxelSystem.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 0440c7f79a..06dc983b96 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1747,33 +1747,33 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g stitchMinimumY = qMin(stitchMinimumY, (int)heightfieldHeight); stitchMaximumY = qMax(stitchMaximumY, (int)heightfieldHeight); } - if (nextHeightfieldHeightX != 0.0f) { + if (nextHeightfieldHeightX != 0.0f && x != stackWidth) { corners |= UPPER_RIGHT_CORNER; stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightX); stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightX); } - if (nextHeightfieldHeightZ != 0.0f) { + if (nextHeightfieldHeightZ != 0.0f && z != stackHeight) { corners |= LOWER_LEFT_CORNER; stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightZ); stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightZ); } - if (nextHeightfieldHeightXZ != 0.0f) { + if (nextHeightfieldHeightXZ != 0.0f && x != stackWidth && z != stackHeight) { corners |= LOWER_RIGHT_CORNER; stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightXZ); stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightXZ); } - bool stitchable = middleX && middleZ && !(corners == NO_CORNERS || corners == ALL_CORNERS); + bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS); bool stitched = false; VoxelPoint cornerPoints[4]; int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (stitchable) { for (unsigned int i = 0; i < sizeof(cornerPoints) / sizeof(cornerPoints[0]); i++) { + if (!(corners & (1 << i))) { + continue; + } int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; const quint16* height = heightLineSrc + offsetZ * width + offsetX; - if (*height == 0) { - continue; - } VoxelPoint& point = cornerPoints[i]; int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; point.vertex = glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step; From 27255fbb4a1185aa369e1f2762a8cc5012cd4d58 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 01:04:45 -0800 Subject: [PATCH 52/67] Hermite display fix. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 06dc983b96..57a3730f0c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1817,7 +1817,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } for (int y = position, end = position + count; y < end; y++) { const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); - if (displayHermite && x != 0 && z != 0) { + if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { glm::vec3 normal; if (entry.hermiteX != 0) { glm::vec3 start = glm::vec3(clampedX + entry.getHermiteX(normal), y, clampedZ) * step; From 52641bd20fc753862b5ce0e90869ea8256d2ba5a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 15:17:45 -0800 Subject: [PATCH 53/67] Fix for buffer crash. --- interface/src/MetavoxelSystem.cpp | 126 +++++++++++++++--------------- interface/src/MetavoxelSystem.h | 16 ++-- 2 files changed, 73 insertions(+), 69 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 57a3730f0c..a566b18b7e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -223,8 +223,8 @@ void MetavoxelSystem::render() { glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); HeightfieldPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); @@ -245,8 +245,8 @@ void MetavoxelSystem::render() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -273,8 +273,8 @@ void MetavoxelSystem::render() { glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); HeightfieldPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); @@ -324,8 +324,8 @@ void MetavoxelSystem::render() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -368,8 +368,8 @@ void MetavoxelSystem::render() { glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); @@ -378,8 +378,8 @@ void MetavoxelSystem::render() { glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -409,8 +409,8 @@ void MetavoxelSystem::render() { glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); @@ -453,8 +453,8 @@ void MetavoxelSystem::render() { glActiveTexture(GL_TEXTURE0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -492,13 +492,13 @@ void MetavoxelSystem::render() { glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); glVertexPointer(3, GL_FLOAT, 0, 0); glDrawArrays(GL_LINES, 0, batch.vertexCount); - batch.vertexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -547,6 +547,12 @@ void MetavoxelSystem::deleteTextures(int heightTextureID, int colorTextureID, in glDeleteTextures(1, (const GLuint*)&materialTextureID); } +void MetavoxelSystem::deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const { + glDeleteBuffers(1, (const GLuint*)&vertexBufferID); + glDeleteBuffers(1, (const GLuint*)&indexBufferID); + glDeleteBuffers(1, (const GLuint*)&hermiteBufferID); +} + class SpannerRenderVisitor : public SpannerVisitor { public: @@ -649,8 +655,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); HeightfieldPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex); @@ -662,8 +668,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glBindTexture(GL_TEXTURE_2D, 0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -687,16 +693,16 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glRotatef(glm::degrees(glm::angle(batch.rotation)), axis.x, axis.y, axis.z); glScalef(batch.scale.x, batch.scale.y, batch.scale.z); - batch.vertexBuffer->bind(); - batch.indexBuffer->bind(); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.indexBufferID); VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); glDrawRangeElements(GL_QUADS, 0, batch.vertexCount - 1, batch.indexCount, GL_UNSIGNED_INT, 0); - batch.vertexBuffer->release(); - batch.indexBuffer->release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glPopMatrix(); } @@ -1033,10 +1039,17 @@ VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector _vertexCount(vertices.size()), _indexCount(indices.size()), _hermiteCount(hermite.size()), - _indexBuffer(QOpenGLBuffer::IndexBuffer), + _vertexBufferID(0), + _indexBufferID(0), + _hermiteBufferID(0), _materials(materials) { } +VoxelBuffer::~VoxelBuffer() { + QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteBuffers", Q_ARG(int, _vertexBufferID), + Q_ARG(int, _indexBufferID), Q_ARG(int, _hermiteBufferID)); +} + bool VoxelBuffer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const { float highest = _size - 1.0f; @@ -1106,16 +1119,16 @@ bool VoxelBuffer::findRayIntersection(const glm::vec3& origin, const glm::vec3& } void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { - if (!_vertexBuffer.isCreated()) { - _vertexBuffer.create(); - _vertexBuffer.bind(); - _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); - _vertexBuffer.release(); + if (_vertexBufferID == 0) { + glGenBuffers(1, &_vertexBufferID); + glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferID); + glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(VoxelPoint), _vertices.constData(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); - _indexBuffer.create(); - _indexBuffer.bind(); - _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); - _indexBuffer.release(); + glGenBuffers(1, &_indexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(int), _indices.constData(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); @@ -1134,8 +1147,8 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation baseBatch.translation = translation; baseBatch.rotation = rotation; baseBatch.scale = scale; - baseBatch.vertexBuffer = &_vertexBuffer; - baseBatch.indexBuffer = &_indexBuffer; + baseBatch.vertexBufferID = _vertexBufferID; + baseBatch.indexBufferID = _indexBufferID; baseBatch.vertexCount = _vertexCount; baseBatch.indexCount = _indexCount; Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); @@ -1145,8 +1158,8 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation splatBatch.translation = translation; splatBatch.rotation = rotation; splatBatch.scale = scale; - splatBatch.vertexBuffer = &_vertexBuffer; - splatBatch.indexBuffer = &_indexBuffer; + splatBatch.vertexBufferID = _vertexBufferID; + splatBatch.indexBufferID = _indexBufferID; splatBatch.vertexCount = _vertexCount; splatBatch.indexCount = _indexCount; splatBatch.splatTextureOffset = glm::vec3( @@ -1178,18 +1191,18 @@ void VoxelBuffer::render(const glm::vec3& translation, const glm::quat& rotation } if (_hermiteCount > 0) { - if (!_hermiteBuffer.isCreated()) { - _hermiteBuffer.create(); - _hermiteBuffer.bind(); - _hermiteBuffer.allocate(_hermite.constData(), _hermite.size() * sizeof(glm::vec3)); - _hermiteBuffer.release(); + if (_hermiteBufferID == 0) { + glGenBuffers(1, &_hermiteBufferID); + glBindBuffer(GL_ARRAY_BUFFER, _hermiteBufferID); + glBufferData(GL_ARRAY_BUFFER, _hermite.size() * sizeof(glm::vec3), _hermite.constData(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); _hermite.clear(); } HermiteBatch hermiteBatch; hermiteBatch.translation = translation; hermiteBatch.rotation = rotation; hermiteBatch.scale = scale; - hermiteBatch.vertexBuffer = &_hermiteBuffer; + hermiteBatch.vertexBufferID = _hermiteBufferID; hermiteBatch.vertexCount = _hermiteCount; Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); } @@ -1741,29 +1754,19 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const int NO_CORNERS = 0; const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; int corners = NO_CORNERS; - int stitchMinimumY = INT_MAX, stitchMaximumY = -1; if (heightfieldHeight != 0.0f) { corners |= UPPER_LEFT_CORNER; - stitchMinimumY = qMin(stitchMinimumY, (int)heightfieldHeight); - stitchMaximumY = qMax(stitchMaximumY, (int)heightfieldHeight); } if (nextHeightfieldHeightX != 0.0f && x != stackWidth) { corners |= UPPER_RIGHT_CORNER; - stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightX); - stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightX); } if (nextHeightfieldHeightZ != 0.0f && z != stackHeight) { corners |= LOWER_LEFT_CORNER; - stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightZ); - stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightZ); } if (nextHeightfieldHeightXZ != 0.0f && x != stackWidth && z != stackHeight) { corners |= LOWER_RIGHT_CORNER; - stitchMinimumY = qMin(stitchMinimumY, (int)nextHeightfieldHeightXZ); - stitchMaximumY = qMax(stitchMaximumY, (int)nextHeightfieldHeightXZ); } bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS); - bool stitched = false; VoxelPoint cornerPoints[4]; int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); if (stitchable) { @@ -2105,7 +2108,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g vertices.append(point); } - if (stitchable && !stitched && y >= stitchMinimumY && y <= stitchMaximumY) { + if (stitchable) { int nextIndex = vertices.size(); const NormalIndex& previousIndexX = lastIndicesX.getClosest(y); const NormalIndex& previousIndexZ = lastIndicesZ[x].getClosest(y); @@ -2270,7 +2273,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g break; } } - stitched = true; } } @@ -2395,8 +2397,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } HeightfieldBaseLayerBatch baseBatch; - baseBatch.vertexBuffer = &bufferPair.first; - baseBatch.indexBuffer = &bufferPair.second; + baseBatch.vertexBufferID = bufferPair.first.bufferId(); + baseBatch.indexBufferID = bufferPair.second.bufferId(); baseBatch.translation = translation; baseBatch.rotation = rotation; baseBatch.scale = scale; @@ -2410,8 +2412,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (!(cursor || _networkTextures.isEmpty())) { HeightfieldSplatBatch splatBatch; - splatBatch.vertexBuffer = &bufferPair.first; - splatBatch.indexBuffer = &bufferPair.second; + splatBatch.vertexBufferID = bufferPair.first.bufferId(); + splatBatch.indexBufferID = bufferPair.second.bufferId(); splatBatch.translation = translation; splatBatch.rotation = rotation; splatBatch.scale = scale; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 15277e8a84..28e5758840 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -82,7 +82,8 @@ public: void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); } Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const; - + Q_INVOKABLE void deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const; + signals: void rendering(); @@ -152,8 +153,8 @@ private: /// Base class for all batches. class MetavoxelBatch { public: - QOpenGLBuffer* vertexBuffer; - QOpenGLBuffer* indexBuffer; + GLuint vertexBufferID; + GLuint indexBufferID; glm::vec3 translation; glm::quat rotation; glm::vec3 scale; @@ -200,7 +201,7 @@ public: /// A batch containing Hermite data for debugging. class HermiteBatch { public: - QOpenGLBuffer* vertexBuffer; + GLuint vertexBufferID; glm::vec3 translation; glm::quat rotation; glm::vec3 scale; @@ -316,6 +317,7 @@ public: VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& hermite, const QMultiHash& quadIndices, int size, const QVector& materials = QVector()); + virtual ~VoxelBuffer(); bool isHermiteEnabled() const { return _hermiteEnabled; } @@ -336,9 +338,9 @@ private: int _vertexCount; int _indexCount; int _hermiteCount; - QOpenGLBuffer _vertexBuffer; - QOpenGLBuffer _indexBuffer; - QOpenGLBuffer _hermiteBuffer; + GLuint _vertexBufferID; + GLuint _indexBufferID; + GLuint _hermiteBufferID; QVector _materials; QVector _networkTextures; }; From a9f7533477d4f2f32f25f12ab2cac070452c2bbb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 16:20:55 -0800 Subject: [PATCH 54/67] Set/erase brushes. --- interface/src/ui/MetavoxelEditor.cpp | 7 +++- interface/src/ui/MetavoxelEditor.h | 1 + .../metavoxels/src/MetavoxelMessages.cpp | 9 +++-- libraries/metavoxels/src/MetavoxelMessages.h | 5 ++- libraries/metavoxels/src/Spanner.cpp | 36 +++++++++++++------ libraries/metavoxels/src/Spanner.h | 9 +++-- 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6bd1ea2438..211655eb97 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -869,11 +869,16 @@ HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor) _height->setMinimum(-FLT_MAX); _height->setMaximum(FLT_MAX); _height->setValue(1.0); + + _form->addRow("Mode:", _mode = new QComboBox()); + _mode->addItem("Raise/Lower"); + _mode->addItem("Set"); + _mode->addItem("Erase"); } QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) { return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(), - alternate ? -_height->value() : _height->value())); + alternate ? -_height->value() : _height->value(), _mode->currentIndex() == 1, _mode->currentIndex() == 2)); } MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) : diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 5ddf582f52..23feb940c9 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -346,6 +346,7 @@ protected: private: QDoubleSpinBox* _height; + QComboBox* _mode; }; /// Contains widgets for editing materials. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index f95efeb359..bae5768068 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -148,10 +148,13 @@ void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects data.set(minimum, this->data, blend); } -PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) : +PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, + float height, bool set, bool erase) : position(position), radius(radius), - height(height) { + height(height), + set(set), + erase(erase) { } void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { @@ -163,7 +166,7 @@ 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); + Spanner* newSpanner = static_cast(spanner.data())->paintHeight(position, radius, height, set, erase); 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 4e478e5109..71013996c2 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -203,8 +203,11 @@ public: STREAM glm::vec3 position; STREAM float radius; STREAM float height; + STREAM bool set; + STREAM bool erase; - PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f); + PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, + float height = 0.0f, bool set = false, bool erase = false); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index be030b6bc4..946c9a9da5 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -111,7 +111,7 @@ 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) { +Spanner* Spanner::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase) { return this; } @@ -1825,7 +1825,8 @@ 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, float normalizeScale, float normalizeOffset) { + const glm::vec3& position, float radius, float height, bool set, bool erase, + float normalizeScale, float normalizeOffset) { if (!_height) { return this; } @@ -1852,7 +1853,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, normalizeScale, normalizeOffset); + nextScale, position, radius, height, set, erase, normalizeScale, normalizeOffset); if (_children[i] != newChild) { if (newNode == this) { newNode = new HeightfieldNode(*this); @@ -1896,16 +1897,22 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& translation, cons float squaredRadiusReciprocal = 1.0f / squaredRadius; float multiplierZ = extents.x / extents.z; float relativeHeight = height * numeric_limits::max() / scale.y; + quint16 heightValue = erase ? 0 : relativeHeight; for (float z = start.z; z <= end.z; z += 1.0f) { quint16* dest = lineDest; for (float x = start.x; x <= end.x; 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; - if (value != 0) { - *dest = value + relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + if (erase || set) { + *dest = heightValue; + + } else { + // height falls off towards edges + int value = *dest; + if (value != 0) { + *dest = value + relativeHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; + } } } } @@ -3352,18 +3359,25 @@ 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) { +Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase) { // first see if we're going to exceed the range limits float minimumValue = 1.0f, maximumValue = numeric_limits::max(); - _root->getRangeAfterHeightPaint(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, - getScale() * _aspectZ), position, radius, height, minimumValue, maximumValue); + if (set) { + float heightValue = height * numeric_limits::max() / (getScale() * _aspectY); + minimumValue = qMin(minimumValue, heightValue); + maximumValue = qMax(maximumValue, heightValue); + + } else if (!erase) { + _root->getRangeAfterHeightPaint(getTranslation(), getRotation(), glm::vec3(getScale(), getScale() * _aspectY, + getScale() * _aspectZ), position, radius, height, minimumValue, maximumValue); + } // normalize if necessary float normalizeScale, normalizeOffset; 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, - normalizeScale, normalizeOffset))); + set, erase, normalizeScale, normalizeOffset))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 48daf380fd..12d04edd14 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -74,8 +74,10 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; /// Attempts to modify the spanner's height. + /// \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); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase); /// Attempts to fill the spanner's height (adding removing volumetric information). /// \return the modified spanner, or this if no modification was performed @@ -693,7 +695,8 @@ public: const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const; HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, - const glm::vec3& position, float radius, float height, float normalizeScale, float normalizeOffset); + const glm::vec3& position, float radius, float height, bool set, bool erase, + float normalizeScale, float normalizeOffset); HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, const glm::vec3& position, float radius); @@ -800,7 +803,7 @@ 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); + virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase); virtual Spanner* fillHeight(const glm::vec3& position, float radius); From 2ed6835426cdd2037af2b861e2f10f0b250f0614 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Sun, 18 Jan 2015 16:30:46 -0800 Subject: [PATCH 55/67] Use alpha channel to indicate holes when importing terrain images. --- libraries/metavoxels/src/Spanner.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 946c9a9da5..67d6fef123 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -658,17 +658,17 @@ void HeightfieldHeightEditor::select() { QMessageBox::warning(this, "Invalid Image", "The selected image could not be read."); return; } - image = image.convertToFormat(QImage::Format_RGB888); + image = image.convertToFormat(QImage::Format_ARGB32); int width = getHeightfieldSize(image.width()) + 2 * HeightfieldHeight::HEIGHT_BORDER; int height = getHeightfieldSize(image.height()) + 2 * HeightfieldHeight::HEIGHT_BORDER; QVector contents(width * height); quint16* dest = contents.data() + (width + 1) * HeightfieldHeight::HEIGHT_BORDER; const float CONVERSION_SCALE = 65534.0f / numeric_limits::max(); for (int i = 0; i < image.height(); i++, dest += width) { - const uchar* src = image.constScanLine(i); - for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, - src += DataBlock::COLOR_BYTES) { - *lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET; + const QRgb* src = (const QRgb*)image.constScanLine(i); + for (quint16* lineDest = dest, *end = dest + image.width(); lineDest != end; lineDest++, src++) { + *lineDest = (qAlpha(*src) < numeric_limits::max()) ? 0 : + (quint16)(qRed(*src) * CONVERSION_SCALE) + CONVERSION_OFFSET; } } emit heightChanged(_height = new HeightfieldHeight(width, contents)); From 990ca8397eb5860a073365d100a4606abd586d0b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 19 Jan 2015 10:23:05 -0800 Subject: [PATCH 56/67] add extra debugging for AddressManager connect to domain --- libraries/networking/src/AddressManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 17cb919b19..fba3861f43 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -389,6 +389,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { _rootPlaceName = hostname; _rootPlaceID = QUuid(); + qDebug() << "Possible domain change required to connect to domain at" << hostname << "on" << port; + emit possibleDomainChangeRequired(hostname, port); } From f0d09860ad3c3ea5bfcfff62a6fa143987645867 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 11:46:12 -0800 Subject: [PATCH 57/67] Removed a couple magic numbers. --- interface/src/ui/MetavoxelEditor.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 211655eb97..1df82a42a0 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -877,8 +877,11 @@ HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor) } QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) { + const int SET_MODE_INDEX = 1; + const int ERASE_MODE_INDEX = 2; return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(), - alternate ? -_height->value() : _height->value(), _mode->currentIndex() == 1, _mode->currentIndex() == 2)); + alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX, + _mode->currentIndex() == ERASE_MODE_INDEX)); } MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) : @@ -987,7 +990,8 @@ HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) : } QVariant HeightfieldFillBrushTool::createEdit(bool alternate) { - if (_mode->currentIndex() == 0) { + const int FILL_MODE_INDEX = 0; + if (_mode->currentIndex() == FILL_MODE_INDEX) { return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value())); } Sphere* sphere = new Sphere(); From 8d4707a874a8f0202d3bf3cd37c0c0c9787092f8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 11:53:53 -0800 Subject: [PATCH 58/67] Removed unused function. --- libraries/metavoxels/src/Spanner.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 67d6fef123..a843bf5ddb 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2131,10 +2131,6 @@ static inline bool isSet(const StackArray& array, int y) { array.getEntryData()[qMax(0, y - array.getPosition())].isSet(); } -static inline bool isSet(const QVector& stackContents, int stackWidth, int x, int y, int z) { - return isSet(stackContents.at(z * stackWidth + x), y); -} - 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) { From 422e4fb85d91a06dbe28f3b5e4ec327cd7c579f7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 12:12:31 -0800 Subject: [PATCH 59/67] Use the closest point on an approximative sphere, rather than the center of the node, to determine the LOD distance. --- libraries/metavoxels/src/AttributeRegistry.cpp | 2 +- libraries/metavoxels/src/MetavoxelData.cpp | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 62305de6db..b23d83de55 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -40,7 +40,7 @@ AttributeRegistry::AttributeRegistry() : _spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) { // our baseline LOD threshold is for voxels; spanners are a different story - const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; + const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 16.0f; _spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER); _spannersAttribute->setUserFacing(true); } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a412659f42..b8b03226d2 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -29,7 +29,9 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) : } bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const { - return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold * multiplier; + float halfSize = size * 0.5f; + return size >= (glm::distance(position, minimum + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * + threshold * multiplier; } bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, @@ -57,7 +59,9 @@ bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float s } bool MetavoxelLOD::shouldSubdivide(const glm::vec2& minimum, float size, float multiplier) const { - return size >= glm::distance(glm::vec2(position), minimum + glm::vec2(size, size) * 0.5f) * threshold * multiplier; + float halfSize = size * 0.5f; + return size >= (glm::distance(glm::vec2(position), minimum + glm::vec2(halfSize, halfSize)) - halfSize) * + threshold * multiplier; } bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size, @@ -1560,8 +1564,9 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int e bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * - visitation.visitor->getLOD().threshold; + float halfSize = visitation.info.size * 0.5f; + visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum + + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold; visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); @@ -1590,8 +1595,9 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * - visitation.visitor->getLOD().threshold; + float halfSize = visitation.info.size * 0.5f; + visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum + + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold; visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); From ed287e9bb2408ea448e8aef3fb2f843fe76d358b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 12:16:06 -0800 Subject: [PATCH 60/67] This function isn't used, either. --- libraries/metavoxels/src/Spanner.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index a843bf5ddb..e890821ec2 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2126,11 +2126,6 @@ void HeightfieldNode::getRangeAfterEdit(const glm::vec3& translation, const glm: } } -static inline bool isSet(const StackArray& array, int y) { - return !array.isEmpty() && y < array.getPosition() + array.getEntryCount() && - array.getEntryData()[qMax(0, y - array.getPosition())].isSet(); -} - 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) { From f8f9c25d381620ff1e2ed0a6d2fcb274ab960eb4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 14:59:28 -0800 Subject: [PATCH 61/67] Back to JPG compression for heightfield colors. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 76 +++++-------------- libraries/networking/src/PacketHeaders.cpp | 2 +- 3 files changed, 20 insertions(+), 60 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index f52cdf3e49..5c4591cf61 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : const char* SAVE_FILE = "/resources/metavoxels.dat"; const int FILE_MAGIC = 0xDADAFACE; -const int FILE_VERSION = 3; +const int FILE_VERSION = 4; void MetavoxelPersister::load() { QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index e890821ec2..b550207ec0 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -681,41 +681,17 @@ void HeightfieldHeightEditor::clear() { } static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE + contents.size(), 0); + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; *header++ = width; *header++ = height; if (!contents.isEmpty()) { - // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) - const uchar* src = (const uchar*)contents.constData(); - uchar* dest = (uchar*)inflated.data() + HEIGHTFIELD_DATA_HEADER_SIZE; - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; - int stride = width * DataBlock::COLOR_BYTES; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - *dest = *src - src[-DataBlock::COLOR_BYTES]; - } - for (int y = 1; y < height; y++) { - *dest++ = *src - src[-stride]; - src++; - *dest++ = *src - src[-stride]; - src++; - *dest++ = *src - src[-stride]; - src++; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - int a = src[-DataBlock::COLOR_BYTES]; - int b = src[-stride]; - int c = src[-stride - DataBlock::COLOR_BYTES]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } + QBuffer buffer(&inflated); + buffer.open(QIODevice::WriteOnly | QIODevice::Append); + QImage((const uchar*)contents.constData(), width, height, width * DataBlock::COLOR_BYTES, + QImage::Format_RGB888).save(&buffer, "JPG"); } return qCompress(inflated); } @@ -727,35 +703,19 @@ static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX offsetY = *header++; width = *header++; height = *header++; - QByteArray contents(inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE, 0); - if (!contents.isEmpty()) { - const uchar* src = (const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; - uchar* dest = (uchar*)contents.data(); - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; - int stride = width * DataBlock::COLOR_BYTES; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - *dest = *src + dest[-DataBlock::COLOR_BYTES]; - } - for (int y = 1; y < height; y++) { - *dest = (*src++) + dest[-stride]; - dest++; - *dest = (*src++) + dest[-stride]; - dest++; - *dest = (*src++) + dest[-stride]; - dest++; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - int a = dest[-DataBlock::COLOR_BYTES]; - int b = dest[-stride]; - int c = dest[-stride - DataBlock::COLOR_BYTES]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } + int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; + if (payloadSize == 0) { + return QByteArray(); + } + QImage image = QImage::fromData((const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE, payloadSize, "JPG"); + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } + QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); + char* dest = contents.data(); + int stride = width * DataBlock::COLOR_BYTES; + for (int y = 0; y < height; y++, dest += stride) { + memcpy(dest, image.constScanLine(y), stride); } return contents; } diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 5b9811a152..110892a106 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 11; + return 12; default: return 0; } From 171fead347f5cf2cb204f2ea24d08ddae66c3328 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 19 Jan 2015 15:55:53 -0800 Subject: [PATCH 62/67] remove log about failure to add to physics engine --- libraries/physics/src/PhysicsEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index e33e1a453c..90fd6c65cd 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -66,7 +66,7 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) { _entityMotionStates.insert(motionState); } else { // We failed to add the entity to the simulation. Probably because we couldn't create a shape for it. - qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; + //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; delete motionState; } } From e5f3d48ffbbdac5fb58923caf9ccc2edf6513f18 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 16:33:41 -0800 Subject: [PATCH 63/67] Fix for splatted textures against empty regions. --- interface/resources/shaders/metavoxel_heightfield_splat.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index bb6b0d6536..180cb653ad 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.frag +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -22,7 +22,7 @@ varying vec4 alphaValues; void main(void) { // blend the splat textures - gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + + gl_FragColor = vec4(gl_Color.rgb, step(0.5, gl_Color.a)) * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y + texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z + texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w); From dafa44570a448cffc50d04c828fa3e8bcf235584 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:15:58 -0800 Subject: [PATCH 64/67] That was not the shader change I wanted to check in. This was. --- interface/resources/shaders/metavoxel_heightfield_splat.frag | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index 180cb653ad..0754987fc3 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.frag +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -22,7 +22,8 @@ varying vec4 alphaValues; void main(void) { // blend the splat textures - gl_FragColor = vec4(gl_Color.rgb, step(0.5, gl_Color.a)) * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + + gl_FragColor = vec4(gl_Color.rgb, step(1.0, gl_Color.a + 1.0 / 512.0)) * + (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y + texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z + texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w); From a16b54a34a9ff0e9a15e8591a30c9dde29ce8735 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:23:31 -0800 Subject: [PATCH 65/67] Let's try not using the "augmented" data, since we don't need it. --- interface/src/MetavoxelSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a566b18b7e..eb2933d113 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -154,7 +154,7 @@ void MetavoxelSystem::simulate(float deltaTime) { } SimulateVisitor simulateVisitor(deltaTime, getLOD()); - guideToAugmented(simulateVisitor); + guide(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { @@ -199,7 +199,7 @@ void MetavoxelSystem::render() { viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); - guideToAugmented(renderVisitor, true); + guide(renderVisitor); if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { glEnableClientState(GL_VERTEX_ARRAY); From 16de6a130bfdc81346f942072b248687fc12df3e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:29:43 -0800 Subject: [PATCH 66/67] Scratch that last change; it wasn't the problem. --- interface/src/MetavoxelSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eb2933d113..a566b18b7e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -154,7 +154,7 @@ void MetavoxelSystem::simulate(float deltaTime) { } SimulateVisitor simulateVisitor(deltaTime, getLOD()); - guide(simulateVisitor); + guideToAugmented(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { @@ -199,7 +199,7 @@ void MetavoxelSystem::render() { viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); - guide(renderVisitor); + guideToAugmented(renderVisitor, true); if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { glEnableClientState(GL_VERTEX_ARRAY); From 35bd1cda8987edc5bf209bfbdfecac7769428869 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 21:52:56 -0800 Subject: [PATCH 67/67] I believe this should fix some problems with flickering/crashing on edits: don't reapply properties that we've already read (thus clearing cached data, stepping on other threads, etc.) --- libraries/metavoxels/src/Bitstream.cpp | 20 +++++++++----- libraries/metavoxels/src/SharedObject.cpp | 4 +-- libraries/metavoxels/src/SharedObject.h | 6 +++-- libraries/metavoxels/src/Spanner.cpp | 32 +++++++++++++++++------ libraries/metavoxels/src/Spanner.h | 4 +-- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c3bd05d3c7..642562bfb5 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -2432,12 +2432,13 @@ void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, } QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { + bool reread = (object != NULL); if (!object && _metaObject) { object = _metaObject->newInstance(); } foreach (const StreamerPropertyPair& property, _properties) { QVariant value = property.first->read(in); - if (property.second.isValid() && object) { + if (property.second.isValid() && object && !reread) { property.second.write(object, value); } } @@ -2445,6 +2446,7 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { } QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { + bool reread = (object != NULL); if (!object && _metaObject) { object = _metaObject->newInstance(); } @@ -2452,7 +2454,7 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere QVariant value; property.first->readDelta(in, value, (property.second.isValid() && reference && reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); - if (property.second.isValid() && object) { + if (property.second.isValid() && object && !reread) { property.second.write(object, value); } } @@ -2475,13 +2477,13 @@ void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const { QObject* result = MappedObjectStreamer::read(in, object); - static_cast(result)->readExtra(in); + static_cast(result)->readExtra(in, object != NULL); return result; } QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object); - static_cast(result)->readExtraDelta(in, static_cast(reference)); + static_cast(result)->readExtraDelta(in, static_cast(reference), object != NULL); return result; } @@ -2592,6 +2594,7 @@ void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, } QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { + bool reread = (object != NULL); if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -2599,11 +2602,14 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { foreach (const StreamerNamePair& property, _properties) { values.append(property.first->read(in)); } - static_cast(object)->setValues(values); + if (!reread) { + static_cast(object)->setValues(values); + } return object; } QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { + bool reread = (object != NULL); if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -2615,7 +2621,9 @@ QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* refer static_cast(reference)->getValues().at(i) : QVariant()); values.append(value); } - static_cast(object)->setValues(values); + if (!reread) { + static_cast(object)->setValues(values); + } return object; } diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index dcfa9732b3..6369037e2a 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -135,7 +135,7 @@ void SharedObject::writeExtra(Bitstream& out) const { // nothing by default } -void SharedObject::readExtra(Bitstream& in) { +void SharedObject::readExtra(Bitstream& in, bool reread) { // nothing by default } @@ -143,7 +143,7 @@ void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference // nothing by default } -void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) { +void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { // nothing by default } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index ebea322bf1..cd46ae9658 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -84,13 +84,15 @@ public: virtual void writeExtra(Bitstream& out) const; /// Reads the non-property contents of this object from the specified stream. - virtual void readExtra(Bitstream& in); + /// \param reread if true, reread the contents from the stream but don't reapply them + virtual void readExtra(Bitstream& in, bool reread = false); /// Writes the delta-encoded non-property contents of this object to the specified stream. virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; /// Reads the delta-encoded non-property contents of this object from the specified stream. - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + /// \param reread if true, reread the contents from the stream but don't reapply them + virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false); /// Writes the subdivision of the contents of this object (preceeded by a /// reference to the object itself) to the specified stream if necessary. diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index b550207ec0..c771b8fb4a 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -3678,9 +3678,18 @@ void Heightfield::writeExtra(Bitstream& out) const { _root->write(state); } -void Heightfield::readExtra(Bitstream& in) { +void Heightfield::readExtra(Bitstream& in, bool reread) { if (getWillBeVoxelized()) { - in >> _height >> _color >> _material >> _stack; + if (reread) { + HeightfieldHeightPointer height; + HeightfieldColorPointer color; + HeightfieldMaterialPointer material; + HeightfieldStackPointer stack; + in >> height >> color >> material >> stack; + + } else { + in >> _height >> _color >> _material >> _stack; + } return; } MetavoxelLOD lod; @@ -3692,7 +3701,9 @@ void Heightfield::readExtra(Bitstream& in) { HeightfieldNodePointer root(new HeightfieldNode()); root->read(state); - setRoot(root); + if (!reread) { + setRoot(root); + } } void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { @@ -3716,7 +3727,7 @@ void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) } } -void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { +void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { MetavoxelLOD lod, referenceLOD; if (in.getContext()) { MetavoxelStreamBase* base = static_cast(in.getContext()); @@ -3726,16 +3737,21 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { HeightfieldStreamBase base = { in, lod, referenceLOD }; HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - setRoot(static_cast(reference)->getRoot()); bool changed; in >> changed; if (changed) { HeightfieldNodePointer root(new HeightfieldNode()); root->readDelta(static_cast(reference)->getRoot(), state); - setRoot(root); - + if (!reread) { + setRoot(root); + } } else if (state.becameSubdividedOrCollapsed()) { - setRoot(HeightfieldNodePointer(_root->readSubdivision(state))); + HeightfieldNodePointer root(_root->readSubdivision(state)); + if (!reread) { + setRoot(root); + } + } else if (!reread) { + setRoot(static_cast(reference)->getRoot()); } } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 12d04edd14..653893c84d 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -820,9 +820,9 @@ public: virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); virtual void writeExtra(Bitstream& out) const; - virtual void readExtra(Bitstream& in); + virtual void readExtra(Bitstream& in, bool reread); virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread); virtual void maybeWriteSubdivision(Bitstream& out); virtual SharedObject* readSubdivision(Bitstream& in);