From 6cd96c95f4e5a90aa52921c9c30ea8f904800db3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Nov 2014 16:26:58 -0800 Subject: [PATCH 01/29] Include a context in the Bitstream so that streamed objects can find their LOD. --- libraries/metavoxels/src/Bitstream.cpp | 1 + libraries/metavoxels/src/Bitstream.h | 8 ++++++++ libraries/metavoxels/src/MetavoxelData.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e9ac3d6319..7bfd9c91ff 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -151,6 +151,7 @@ Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, Generic _position(0), _metadataType(metadataType), _genericsMode(genericsMode), + _context(NULL), _objectStreamerStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 1fd9205387..8e40ed9240 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -342,6 +342,12 @@ public: /// Returns a reference to the underlying data stream. QDataStream& getUnderlying() { return _underlying; } + /// Sets the context pointer. + void setContext(void* context) { _context = context; } + + /// Returns the context pointer. + void* getContext() const { return _context; } + /// Substitutes the supplied metaobject for the given class name's default mapping. This is mostly useful for testing the /// process of mapping between different types, but may in the future be used for permanently renaming classes. void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); @@ -562,6 +568,8 @@ private: MetadataType _metadataType; GenericsMode _genericsMode; + void* _context; + RepeatedValueStreamer _objectStreamerStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index c340a7dd4a..2f20899ffb 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -577,7 +577,9 @@ void MetavoxelData::read(Bitstream& in, const MetavoxelLOD& lod) { } MetavoxelStreamBase base = { attribute, in, lod, lod }; MetavoxelStreamState state = { base, getMinimum(), _size }; + in.setContext(&base); attribute->readMetavoxelRoot(*this, state); + in.setContext(NULL); } } @@ -587,7 +589,9 @@ void MetavoxelData::write(Bitstream& out, const MetavoxelLOD& lod) const { out << it.key(); MetavoxelStreamBase base = { it.key(), out, lod, lod }; MetavoxelStreamState state = { base, getMinimum(), _size }; + out.setContext(&base); it.key()->writeMetavoxelRoot(*it.value(), state); + out.setContext(NULL); } out << AttributePointer(); } @@ -622,6 +626,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD MetavoxelStreamBase base = { attribute, in, lod, referenceLOD }; MetavoxelStreamState state = { base, minimum, _size }; MetavoxelNode* oldRoot = _roots.value(attribute); + in.setContext(&base); if (oldRoot) { bool changed; in >> changed; @@ -637,6 +642,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD } else { attribute->readMetavoxelRoot(*this, state); } + in.setContext(NULL); } forever { @@ -657,7 +663,9 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD it != remainingRoots.constEnd(); it++) { MetavoxelStreamBase base = { it.key(), in, lod, referenceLOD }; MetavoxelStreamState state = { base, minimum, _size }; + in.setContext(&base); it.key()->readMetavoxelSubdivision(*this, state); + in.setContext(NULL); } } } @@ -693,6 +701,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key()); MetavoxelStreamBase base = { it.key(), out, lod, referenceLOD }; MetavoxelStreamState state = { base, minimum, _size }; + out.setContext(&base); if (it.value() != referenceRoot || becameSubdivided) { out << it.key(); if (referenceRoot) { @@ -707,6 +716,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO it.key()->writeMetavoxelRoot(*it.value(), state); } } + out.setContext(NULL); } out << AttributePointer(); From 282732677fb7be2ce4393a7f724a8dfe2699b089 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Nov 2014 16:45:39 -0800 Subject: [PATCH 02/29] Catch and log BitstreamExceptions when reading datagrams. --- .../metavoxels/src/DatagramSequencer.cpp | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index d97ed67644..1aeef8e450 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -239,39 +239,44 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) { _sendRecords.erase(_sendRecords.begin(), it + 1); } - // alert external parties so that they can read the middle - emit readyToRead(_inputStream); - - // read and dispatch the high-priority messages - int highPriorityMessageCount; - _inputStream >> highPriorityMessageCount; - int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; - for (int i = 0; i < highPriorityMessageCount; i++) { - QVariant data; - _inputStream >> data; - if (i >= _receivedHighPriorityMessages) { - emit receivedHighPriorityMessage(data); + try { + // alert external parties so that they can read the middle + emit readyToRead(_inputStream); + + // read and dispatch the high-priority messages + int highPriorityMessageCount; + _inputStream >> highPriorityMessageCount; + int newHighPriorityMessages = highPriorityMessageCount - _receivedHighPriorityMessages; + for (int i = 0; i < highPriorityMessageCount; i++) { + QVariant data; + _inputStream >> data; + if (i >= _receivedHighPriorityMessages) { + emit receivedHighPriorityMessage(data); + } } - } - _receivedHighPriorityMessages = highPriorityMessageCount; + _receivedHighPriorityMessages = highPriorityMessageCount; + + // read the reliable data, if any + quint32 reliableChannels; + _incomingPacketStream >> reliableChannels; + for (quint32 i = 0; i < reliableChannels; i++) { + quint32 channelIndex; + _incomingPacketStream >> channelIndex; + getReliableInputChannel(channelIndex)->readData(_incomingPacketStream); + } + + // record the receipt + ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; + _receiveRecords.append(record); + + emit receiveRecorded(); - // read the reliable data, if any - quint32 reliableChannels; - _incomingPacketStream >> reliableChannels; - for (quint32 i = 0; i < reliableChannels; i++) { - quint32 channelIndex; - _incomingPacketStream >> channelIndex; - getReliableInputChannel(channelIndex)->readData(_incomingPacketStream); + } catch (const BitstreamException& e) { + qWarning() << "Error reading datagram:" << e.getDescription(); } _incomingPacketStream.device()->seek(0); - _inputStream.reset(); - - // record the receipt - ReceiveRecord record = { _incomingPacketNumber, _inputStream.getAndResetReadMappings(), newHighPriorityMessages }; - _receiveRecords.append(record); - - emit receiveRecorded(); + _inputStream.reset(); } void DatagramSequencer::sendClearSharedObjectMessage(int id) { From af875eb5af43cf9748fcb1fb82e7aec2f994c4bc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 24 Nov 2014 16:45:34 -0800 Subject: [PATCH 03/29] Allow shared objects to write extra, non-property data. --- libraries/metavoxels/src/Bitstream.cpp | 46 ++++++++++++++++++++--- libraries/metavoxels/src/Bitstream.h | 12 ++++++ libraries/metavoxels/src/SharedObject.cpp | 16 ++++++++ libraries/metavoxels/src/SharedObject.h | 15 +++++++- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 7bfd9c91ff..e4af43feee 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1156,6 +1156,16 @@ Bitstream& Bitstream::operator<(const ObjectStreamer* streamer) { return *this; } +static MappedObjectStreamer* createMappedObjectStreamer(const QMetaObject* metaObject, + const QVector& properties) { + for (const QMetaObject* super = metaObject; super; super = super->superClass()) { + if (super == &SharedObject::staticMetaObject) { + return new SharedObjectStreamer(metaObject, properties); + } + } + return new MappedObjectStreamer(metaObject, properties); +} + Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { QByteArray className; *this >> className; @@ -1231,7 +1241,7 @@ Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { } else if (metaObject) { const QVector& localProperties = streamer->getProperties(); if (localProperties.size() != properties.size()) { - streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); return *this; } for (int i = 0; i < localProperties.size(); i++) { @@ -1239,13 +1249,13 @@ Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { const StreamerPropertyPair& localProperty = localProperties.at(i); if (property.first != localProperty.first || property.second.propertyIndex() != localProperty.second.propertyIndex()) { - streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); return *this; } } return *this; } - streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); return *this; } @@ -1671,7 +1681,7 @@ QHash Bitstream::createObjectStreamer properties.append(StreamerPropertyPair(streamer->getSelf(), property)); } } - ObjectStreamerPointer streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + ObjectStreamerPointer streamer = ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties)); streamer->_self = streamer; objectStreamers.insert(metaObject, streamer.data()); } @@ -2122,7 +2132,7 @@ JSONReader::JSONReader(const QJsonDocument& document, Bitstream::GenericsMode ge if (matches) { _objectStreamers.insert(name, baseStreamer->getSelf()); } else { - _objectStreamers.insert(name, ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties))); + _objectStreamers.insert(name, ObjectStreamerPointer(createMappedObjectStreamer(metaObject, properties))); } } @@ -2437,6 +2447,32 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere return object; } +SharedObjectStreamer::SharedObjectStreamer(const QMetaObject* metaObject, const QVector& properties) : + MappedObjectStreamer(metaObject, properties) { +} + +void SharedObjectStreamer::write(Bitstream& out, const QObject* object) const { + MappedObjectStreamer::write(out, object); + static_cast(object)->writeExtra(out); +} + +void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { + MappedObjectStreamer::writeRawDelta(out, object, reference); + static_cast(object)->writeExtraDelta(out, static_cast(reference)); +} + +QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const { + QObject* result = MappedObjectStreamer::read(in, object); + static_cast(result)->readExtra(in); + 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)); + return result; +} + GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash) : ObjectStreamer(&GenericSharedObject::staticMetaObject), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 8e40ed9240..e5aa30fac5 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -1133,6 +1133,18 @@ private: QVector _properties; }; +/// A streamer that maps to a local shared object class. Shared objects can write extra, non-property data. +class SharedObjectStreamer : public MappedObjectStreamer { +public: + + SharedObjectStreamer(const QMetaObject* metaObject, const QVector& properties); + + virtual void write(Bitstream& out, const QObject* object) const; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; +}; + typedef QPair StreamerNamePair; /// A streamer for generic objects. diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index bf9b123a36..e57b7d9a8e 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -131,6 +131,22 @@ void SharedObject::dump(QDebug debug) const { } } +void SharedObject::writeExtra(Bitstream& out) const { + // nothing by default +} + +void SharedObject::readExtra(Bitstream& in) { + // nothing by default +} + +void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { + // nothing by default +} + +void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) { + // nothing by default +} + QAtomicInt SharedObject::_nextID(1); WeakSharedObjectHash SharedObject::_weakHash; QReadWriteLock SharedObject::_weakHashLock; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 157987ed6f..7f44ffec82 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -24,6 +24,7 @@ class QComboBox; +class Bitstream; class SharedObject; typedef QHash > WeakSharedObjectHash; @@ -76,9 +77,21 @@ public: /// this is an instance of a superclass of the other object's class) rather than simply returning false. virtual bool equals(const SharedObject* other, bool sharedAncestry = false) const; - // Dumps the contents of this object to the debug output. + /// Dumps the contents of this object to the debug output. virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const; + /// Writes the non-property contents of this object to the specified stream. + virtual void writeExtra(Bitstream& out) const; + + /// Reads the non-property contents of this object from the specified stream. + virtual void readExtra(Bitstream& in); + + /// 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); + private: int _id; From 8d3f4a627b0d5ecce94cf67d41205bcc6efe04f5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Nov 2014 14:10:44 -0800 Subject: [PATCH 04/29] Working on quadtree for heightfields. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- libraries/metavoxels/src/MetavoxelData.cpp | 28 ++ libraries/metavoxels/src/MetavoxelData.h | 11 + .../metavoxels/src/MetavoxelMessages.cpp | 6 +- libraries/metavoxels/src/Spanner.cpp | 372 +++++++++++++++++- libraries/metavoxels/src/Spanner.h | 82 ++++ libraries/networking/src/PacketHeaders.cpp | 2 +- 7 files changed, 496 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 314ffb28e4..89b3102391 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 = 1; +const int FILE_VERSION = 2; void MetavoxelPersister::load() { QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2f20899ffb..860ab3e5e9 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -56,6 +56,34 @@ bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float s return true; } +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; +} + +bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size, + const MetavoxelLOD& reference, float multiplier) const { + if (position == reference.position && threshold >= reference.threshold) { + return false; // first off, nothing becomes subdivided if it doesn't change + } + if (!shouldSubdivide(minimum, size, multiplier)) { + return false; // this one must be subdivided + } + // TODO: find some way of culling subtrees that can't possibly contain subdivided nodes + return true; +} + +bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec2& minimum, float size, + const MetavoxelLOD& reference, float multiplier) const { + if (position == reference.position && threshold == reference.threshold) { + return false; // first off, nothing becomes subdivided or collapsed if it doesn't change + } + if (!(shouldSubdivide(minimum, size, multiplier) || reference.shouldSubdivide(minimum, size, multiplier))) { + return false; // this one or the reference must be subdivided + } + // TODO: find some way of culling subtrees that can't possibly contain subdivided or collapsed nodes + return true; +} + MetavoxelData::MetavoxelData() : _size(1.0f) { } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 7bfd2a7522..56d9dd3a8a 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -53,6 +53,17 @@ public: /// enabled or disabled as compared to the reference. bool becameSubdividedOrCollapsed(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; + + /// Checks whether, according to this LOD, we should subdivide the described region. + bool shouldSubdivide(const glm::vec2& minimum, float size, float multiplier = 1.0f) const; + + /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. + bool becameSubdivided(const glm::vec2& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const; + + /// Checks whether the node or any of the nodes underneath it have had subdivision + /// enabled or disabled as compared to the reference. + bool becameSubdividedOrCollapsed(const glm::vec2& minimum, float size, + const MetavoxelLOD& reference, float multiplier = 1.0f) const; }; DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index a4d2569de0..1225752df7 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -123,11 +123,9 @@ RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { SharedObject* object = objects.value(id); - if (!object) { - qDebug() << "Missing object to remove" << id; - return; + if (object) { + data.remove(attribute, object); } - data.remove(attribute, object); } ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) : diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 617d753414..7c58b7f297 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -24,6 +24,7 @@ #include +#include "MetavoxelData.h" #include "Spanner.h" using namespace std; @@ -1107,9 +1108,306 @@ template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const } } +bool HeightfieldStreamState::shouldSubdivide() const { + return base.lod.shouldSubdivide(minimum, size); +} + +bool HeightfieldStreamState::shouldSubdivideReference() const { + return base.referenceLOD.shouldSubdivide(minimum, size); +} + +bool HeightfieldStreamState::becameSubdivided() const { + return base.lod.becameSubdivided(minimum, size, base.referenceLOD); +} + +bool HeightfieldStreamState::becameSubdividedOrCollapsed() const { + return base.lod.becameSubdividedOrCollapsed(minimum, size, base.referenceLOD); +} + +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; +const int MAXIMUM_FLAG_MASK = X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG; + +static glm::vec2 getNextMinimum(const glm::vec2& minimum, float nextSize, int index) { + return minimum + glm::vec2( + (index & X_MAXIMUM_FLAG) ? nextSize : 0.0f, + (index & Y_MAXIMUM_FLAG) ? nextSize : 0.0f); +} + +void HeightfieldStreamState::setMinimum(const glm::vec2& lastMinimum, int index) { + minimum = getNextMinimum(lastMinimum, size, index); +} + +HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, + const HeightfieldMaterialPointer& material) : + _height(height), + _color(color), + _material(material) { +} + +bool HeightfieldNode::isLeaf() const { + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i]) { + return false; + } + } + return true; +} + +void HeightfieldNode::read(HeightfieldStreamState& state) { + clearChildren(); + + if (!state.shouldSubdivide()) { + state.base.stream >> _height >> _color >> _material; + return; + } + bool leaf; + state.base.stream >> leaf; + if (leaf) { + state.base.stream >> _height >> _color >> _material; + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i] = new HeightfieldNode(); + _children[i]->read(nextState); + } + mergeChildren(); + } +} + +void HeightfieldNode::write(HeightfieldStreamState& state) const { + if (!state.shouldSubdivide()) { + state.base.stream << _height << _color << _material; + return; + } + bool leaf = isLeaf(); + state.base.stream << leaf; + if (leaf) { + state.base.stream << _height << _color << _material; + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->write(nextState); + } + } +} + +void HeightfieldNode::readDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) { + clearChildren(); + + if (!state.shouldSubdivide()) { + state.base.stream.readDelta(_height, reference->getHeight()); + state.base.stream.readDelta(_color, reference->getColor()); + state.base.stream.readDelta(_material, reference->getMaterial()); + return; + } + bool leaf; + state.base.stream >> leaf; + if (leaf) { + state.base.stream.readDelta(_height, reference->getHeight()); + state.base.stream.readDelta(_color, reference->getColor()); + state.base.stream.readDelta(_material, reference->getMaterial()); + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + if (reference->isLeaf() || !state.shouldSubdivideReference()) { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i] = new HeightfieldNode(); + _children[i]->read(nextState); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + bool changed; + state.base.stream >> changed; + if (changed) { + _children[i] = new HeightfieldNode(); + _children[i]->readDelta(reference->getChild(i), nextState); + } else { + if (nextState.becameSubdividedOrCollapsed()) { + _children[i] = reference->getChild(i)->readSubdivision(nextState); + + } else { + _children[i] = reference->getChild(i); + } + } + } + } + mergeChildren(); + } +} + +void HeightfieldNode::writeDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) const { + if (!state.shouldSubdivide()) { + state.base.stream.writeDelta(_height, reference->getHeight()); + state.base.stream.writeDelta(_color, reference->getColor()); + state.base.stream.writeDelta(_material, reference->getMaterial()); + return; + } + bool leaf = isLeaf(); + state.base.stream << leaf; + if (leaf) { + state.base.stream.writeDelta(_height, reference->getHeight()); + state.base.stream.writeDelta(_color, reference->getColor()); + state.base.stream.writeDelta(_material, reference->getMaterial()); + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + if (reference->isLeaf() || !state.shouldSubdivideReference()) { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->write(nextState); + } + } else { + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (_children[i] == reference->getChild(i)) { + state.base.stream << false; + if (nextState.becameSubdivided()) { + _children[i]->writeSubdivision(nextState); + } + } else { + state.base.stream << true; + _children[i]->writeDelta(reference->getChild(i), nextState); + } + } + } + } +} + +HeightfieldNode* HeightfieldNode::readSubdivision(HeightfieldStreamState& state) { + if (state.shouldSubdivide()) { + if (!state.shouldSubdivideReference()) { + bool leaf; + state.base.stream >> leaf; + if (leaf) { + return isLeaf() ? this : new HeightfieldNode(_height, _color, _material); + + } else { + HeightfieldNode* newNode = new HeightfieldNode(_height, _color, _material); + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + newNode->_children[i] = new HeightfieldNode(); + newNode->_children[i]->readSubdivided(nextState, state, this); + } + return newNode; + } + } else if (!isLeaf()) { + HeightfieldNode* node = this; + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdividedOrCollapsed()) { + HeightfieldNode* child = _children[i]->readSubdivision(nextState); + if (_children[i] != child) { + if (node == this) { + node = new HeightfieldNode(*this); + } + node->_children[i] = child; + } + } + } + if (node != this) { + node->mergeChildren(); + } + return node; + } + } else if (!isLeaf()) { + return new HeightfieldNode(_height, _color, _material); + } + return this; +} + +void HeightfieldNode::writeSubdivision(HeightfieldStreamState& state) const { + if (!state.shouldSubdivide()) { + return; + } + bool leaf = isLeaf(); + if (!state.shouldSubdivideReference()) { + state.base.stream << leaf; + if (!leaf) { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSubdivided(nextState, state, this); + } + } + } else if (!leaf) { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->writeSubdivision(nextState); + } + } + } +} + +void HeightfieldNode::readSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, + const HeightfieldNode* ancestor) { + clearChildren(); + + if (!state.shouldSubdivide()) { + // TODO: subdivision encoding + state.base.stream >> _height >> _color >> _material; + return; + } + bool leaf; + state.base.stream >> leaf; + if (leaf) { + state.base.stream >> _height >> _color >> _material; + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i] = new HeightfieldNode(); + _children[i]->readSubdivided(nextState, ancestorState, ancestor); + } + mergeChildren(); + } +} + +void HeightfieldNode::writeSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, + const HeightfieldNode* ancestor) const { + if (!state.shouldSubdivide()) { + // TODO: subdivision encoding + state.base.stream << _height << _color << _material; + return; + } + bool leaf = isLeaf(); + state.base.stream << leaf; + if (leaf) { + state.base.stream << _height << _color << _material; + + } else { + HeightfieldStreamState nextState = { state.base, glm::vec2(), state.size * 0.5f }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + _children[i]->writeSubdivided(nextState, ancestorState, ancestor); + } + } +} + +void HeightfieldNode::clearChildren() { + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i].reset(); + } +} + +void HeightfieldNode::mergeChildren() { +} + Heightfield::Heightfield() : _aspectY(1.0f), - _aspectZ(1.0f) { + _aspectZ(1.0f), + _root(new HeightfieldNode()) { connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds); connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds); @@ -2115,6 +2413,69 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float return false; } +void Heightfield::writeExtra(Bitstream& out) const { + MetavoxelLOD lod; + if (out.getContext()) { + lod = transformLOD(static_cast(out.getContext())->lod); + } + HeightfieldStreamBase base = { out, lod, lod }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + _root->write(state); +} + +void Heightfield::readExtra(Bitstream& in) { + MetavoxelLOD lod; + if (in.getContext()) { + lod = transformLOD(static_cast(in.getContext())->lod); + } + HeightfieldStreamBase base = { in, lod, lod }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + _root = new HeightfieldNode(); + _root->read(state); +} + +void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { + MetavoxelLOD lod, referenceLOD; + if (out.getContext()) { + MetavoxelStreamBase* base = static_cast(out.getContext()); + lod = transformLOD(base->lod); + referenceLOD = transformLOD(base->referenceLOD); + } + HeightfieldStreamBase base = { out, lod, referenceLOD }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + const HeightfieldNodePointer& referenceRoot = static_cast(reference)->getRoot(); + if (_root == referenceRoot) { + out << false; + if (state.becameSubdivided()) { + _root->writeSubdivision(state); + } + } else { + out << true; + _root->writeDelta(referenceRoot, state); + } +} + +void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { + MetavoxelLOD lod, referenceLOD; + if (in.getContext()) { + MetavoxelStreamBase* base = static_cast(in.getContext()); + lod = transformLOD(base->lod); + referenceLOD = transformLOD(base->referenceLOD); + } + HeightfieldStreamBase base = { in, lod, referenceLOD }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + + bool changed; + in >> changed; + if (changed) { + _root = new HeightfieldNode(); + _root->readDelta(static_cast(reference)->getRoot(), state); + + } else if (state.becameSubdividedOrCollapsed()) { + _root = _root->readSubdivision(state); + } +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } @@ -2124,3 +2485,12 @@ void Heightfield::updateBounds() { glm::mat4 rotationMatrix = glm::mat4_cast(getRotation()); setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(glm::vec3(), extent)); } + +MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { + // after transforming into unit space, we scale the threshold in proportion to vertical distance + glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); + glm::vec3 position = glm::inverse(getRotation()) * (lod.position - getTranslation()) * inverseScale; + const float THRESHOLD_MULTIPLIER = 2.0f; + return MetavoxelLOD(glm::vec3(position.x, position.z, 0.0f), lod.threshold * + qMax(0.5f, glm::abs(position.y - 0.5f)) * THRESHOLD_MULTIPLIER); +} diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 7fe32b56a6..bb79a6fc51 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -20,6 +20,7 @@ class HeightfieldColor; class HeightfieldHeight; class HeightfieldMaterial; +class HeightfieldNode; class SpannerRenderer; /// An object that spans multiple octree cells. @@ -458,6 +459,75 @@ 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 HeightfieldNodePointer; + +/// Holds the base state used in streaming heightfield data. +class HeightfieldStreamBase { +public: + Bitstream& stream; + const MetavoxelLOD& lod; + const MetavoxelLOD& referenceLOD; +}; + +/// Holds the state used in streaming a heightfield node. +class HeightfieldStreamState { +public: + HeightfieldStreamBase& base; + glm::vec2 minimum; + float size; + + bool shouldSubdivide() const; + bool shouldSubdivideReference() const; + bool becameSubdivided() const; + bool becameSubdividedOrCollapsed() const; + + void setMinimum(const glm::vec2& lastMinimum, int index); +}; + +/// A node in a heightfield quadtree. +class HeightfieldNode : public QSharedData { +public: + + static const int CHILD_COUNT = 4; + + HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(), + const HeightfieldColorPointer& color = HeightfieldColorPointer(), + const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer()); + + const HeightfieldHeightPointer& getHeight() const { return _height; } + const HeightfieldColorPointer& getColor() const { return _color; } + const HeightfieldMaterialPointer& getMaterial() const { return _material; } + + bool isLeaf() const; + + const HeightfieldNodePointer& getChild(int index) const { return _children[index]; } + + void read(HeightfieldStreamState& state); + void write(HeightfieldStreamState& state) const; + + void readDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state); + void writeDelta(const HeightfieldNodePointer& reference, HeightfieldStreamState& state) const; + + HeightfieldNode* readSubdivision(HeightfieldStreamState& state); + void writeSubdivision(HeightfieldStreamState& state) const; + + void readSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, + const HeightfieldNode* ancestor); + void writeSubdivided(HeightfieldStreamState& state, const HeightfieldStreamState& ancestorState, + const HeightfieldNode* ancestor) const; + +private: + + void clearChildren(); + void mergeChildren(); + + HeightfieldHeightPointer _height; + HeightfieldColorPointer _color; + HeightfieldMaterialPointer _material; + + HeightfieldNodePointer _children[CHILD_COUNT]; +}; + /// A heightfield represented as a spanner. class Heightfield : public Transformable { Q_OBJECT @@ -486,6 +556,8 @@ public: void setMaterial(const HeightfieldMaterialPointer& material); const HeightfieldMaterialPointer& getMaterial() const { return _material; } + const HeightfieldNodePointer& getRoot() const { return _root; } + virtual bool isHeightfield() const; virtual float getHeight(const glm::vec3& location) const; @@ -508,6 +580,11 @@ public: virtual bool contains(const glm::vec3& point); 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 writeExtraDelta(Bitstream& out, const SharedObject* reference) const; + virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + signals: void aspectYChanged(float aspectY); @@ -526,11 +603,16 @@ private slots: private: + MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; + float _aspectY; float _aspectZ; + HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; + + HeightfieldNodePointer _root; }; #endif // hifi_Spanner_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index b1c47c0ebf..3f08cdec69 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 9; + return 10; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From a987e7ce10f0283682eab01d77a21e4802a64f65 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Nov 2014 15:32:02 -0800 Subject: [PATCH 05/29] When we set heightfield layers, break it up into quadtree. --- libraries/metavoxels/src/Spanner.cpp | 103 ++++++++++++++++++++++++++- libraries/metavoxels/src/Spanner.h | 11 ++- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 7c58b7f297..a57cc8a31c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1145,6 +1145,94 @@ HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const H _material(material) { } +const int HEIGHT_LEAF_SIZE = 256 + HeightfieldHeight::HEIGHT_EXTENSION; + +void HeightfieldNode::setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, + const HeightfieldMaterialPointer& material) { + clearChildren(); + + int heightWidth = height->getWidth(); + if (heightWidth <= HEIGHT_LEAF_SIZE) { + _height = height; + _color = color; + _material = material; + return; + } + int heightHeight = height->getContents().size() / heightWidth; + int innerChildHeightWidth = (heightWidth - HeightfieldHeight::HEIGHT_EXTENSION) / 2; + int innerChildHeightHeight = (heightHeight - HeightfieldHeight::HEIGHT_EXTENSION) / 2; + int childHeightWidth = innerChildHeightWidth + HeightfieldHeight::HEIGHT_EXTENSION; + int childHeightHeight = innerChildHeightHeight + HeightfieldHeight::HEIGHT_EXTENSION; + + for (int i = 0; i < CHILD_COUNT; i++) { + QVector childHeightContents(childHeightWidth * childHeightHeight); + quint16* heightDest = childHeightContents.data(); + bool maximumX = (i & X_MAXIMUM_FLAG), maximumY = (i & Y_MAXIMUM_FLAG); + const quint16* heightSrc = height->getContents().constData() + (maximumY ? innerChildHeightHeight * heightWidth : 0) + + (maximumX ? innerChildHeightWidth : 0); + for (int z = 0; z < childHeightHeight; z++, heightDest += childHeightWidth, heightSrc += heightWidth) { + memcpy(heightDest, heightSrc, childHeightWidth * sizeof(quint16)); + } + + HeightfieldColorPointer childColor; + if (color) { + int colorWidth = color->getWidth(); + int colorHeight = color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); + int innerChildColorWidth = (colorWidth - HeightfieldData::SHARED_EDGE) / 2; + int innerChildColorHeight = (colorHeight - HeightfieldData::SHARED_EDGE) / 2; + int childColorWidth = innerChildColorWidth + HeightfieldData::SHARED_EDGE; + int childColorHeight = innerChildColorHeight + HeightfieldData::SHARED_EDGE; + QByteArray childColorContents(childColorWidth * childColorHeight * DataBlock::COLOR_BYTES, 0); + char* dest = childColorContents.data(); + const char* src = color->getContents().constData() + ((maximumY ? innerChildColorHeight * colorWidth : 0) + + (maximumX ? innerChildColorWidth : 0)) * DataBlock::COLOR_BYTES; + for (int z = 0; z < childColorHeight; z++, dest += childColorWidth * DataBlock::COLOR_BYTES, + src += colorWidth * DataBlock::COLOR_BYTES) { + memcpy(dest, src, childColorWidth * DataBlock::COLOR_BYTES); + } + childColor = new HeightfieldColor(childColorWidth, childColorContents); + } + + HeightfieldMaterialPointer childMaterial; + if (material) { + int materialWidth = material->getWidth(); + int materialHeight = material->getContents().size() / materialWidth; + int innerChildMaterialWidth = (materialWidth - HeightfieldData::SHARED_EDGE) / 2; + int innerChildMaterialHeight = (materialHeight - HeightfieldData::SHARED_EDGE) / 2; + int childMaterialWidth = innerChildMaterialWidth + HeightfieldData::SHARED_EDGE; + int childMaterialHeight = innerChildMaterialHeight + HeightfieldData::SHARED_EDGE; + QByteArray childMaterialContents(childMaterialWidth * childMaterialHeight, 0); + QVector childMaterials; + uchar* dest = (uchar*)childMaterialContents.data(); + const uchar* src = (const uchar*)material->getContents().data() + + (maximumY ? innerChildMaterialHeight * materialWidth : 0) + (maximumX ? innerChildMaterialWidth : 0); + QHash materialMap; + for (int z = 0; z < childMaterialHeight; z++, dest += childMaterialWidth, src += materialWidth) { + const uchar* lineSrc = src; + for (uchar* lineDest = dest, *end = dest + childMaterialWidth; lineDest != end; lineDest++, lineSrc++) { + int value = *lineSrc; + if (value != 0) { + int& mapping = materialMap[value]; + if (mapping == 0) { + childMaterials.append(material->getMaterials().at(value - 1)); + mapping = childMaterials.size(); + } + value = mapping; + } + *lineDest = value; + } + } + childMaterial = new HeightfieldMaterial(childMaterialWidth, childMaterialContents, childMaterials); + } + + _children[i] = new HeightfieldNode(); + _children[i]->setContents(HeightfieldHeightPointer(new HeightfieldHeight(childHeightWidth, childHeightContents)), + childColor, childMaterial); + } + + mergeChildren(); +} + bool HeightfieldNode::isLeaf() const { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { @@ -1406,8 +1494,7 @@ void HeightfieldNode::mergeChildren() { Heightfield::Heightfield() : _aspectY(1.0f), - _aspectZ(1.0f), - _root(new HeightfieldNode()) { + _aspectZ(1.0f) { connect(this, &Heightfield::translationChanged, this, &Heightfield::updateBounds); connect(this, &Heightfield::rotationChanged, this, &Heightfield::updateBounds); @@ -1415,6 +1502,11 @@ Heightfield::Heightfield() : connect(this, &Heightfield::aspectYChanged, this, &Heightfield::updateBounds); connect(this, &Heightfield::aspectZChanged, this, &Heightfield::updateBounds); updateBounds(); + + connect(this, &Heightfield::heightChanged, this, &Heightfield::updateRoot); + connect(this, &Heightfield::colorChanged, this, &Heightfield::updateRoot); + connect(this, &Heightfield::materialChanged, this, &Heightfield::updateRoot); + updateRoot(); } void Heightfield::setAspectY(float aspectY) { @@ -2486,6 +2578,13 @@ void Heightfield::updateBounds() { setBounds(glm::translate(getTranslation()) * rotationMatrix * Box(glm::vec3(), extent)); } +void Heightfield::updateRoot() { + _root = new HeightfieldNode(); + if (_height) { + _root->setContents(_height, _color, _material); + } +} + MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { // after transforming into unit space, we scale the threshold in proportion to vertical distance glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index bb79a6fc51..d67c23909d 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -494,6 +494,9 @@ public: const HeightfieldColorPointer& color = HeightfieldColorPointer(), const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer()); + void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, + const HeightfieldMaterialPointer& material); + const HeightfieldHeightPointer& getHeight() const { return _height; } const HeightfieldColorPointer& getColor() const { return _color; } const HeightfieldMaterialPointer& getMaterial() const { return _material; } @@ -533,9 +536,10 @@ class Heightfield : public Transformable { Q_OBJECT Q_PROPERTY(float aspectY MEMBER _aspectY WRITE setAspectY NOTIFY aspectYChanged) Q_PROPERTY(float aspectZ MEMBER _aspectZ WRITE setAspectZ NOTIFY aspectZChanged) - Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged) - Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged DESIGNABLE false) + Q_PROPERTY(HeightfieldHeightPointer height MEMBER _height WRITE setHeight NOTIFY heightChanged STORED false) + Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false) + Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false + DESIGNABLE false) public: @@ -600,6 +604,7 @@ protected: private slots: void updateBounds(); + void updateRoot(); private: From 7f14a6692233210aee91eee27c1bf48102efe8f3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Nov 2014 18:27:09 -0800 Subject: [PATCH 06/29] More work on heightfield quadtrees. --- interface/src/ui/MetavoxelEditor.cpp | 67 +------------ libraries/metavoxels/src/Spanner.cpp | 145 +++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 63 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b6b43c4baf..cf28a79995 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -792,76 +792,17 @@ void ImportHeightfieldTool::renderPreview() { static_cast(_spanner.data())->getRenderer()->render(); } -const int HEIGHTFIELD_BLOCK_SIZE = 256; - void ImportHeightfieldTool::apply() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!(_height->getHeight() && attribute)) { return; } - int width = _height->getHeight()->getWidth(); - const QVector& contents = _height->getHeight()->getContents(); - int height = contents.size() / width; - int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = height - HeightfieldHeight::HEIGHT_EXTENSION; - float scale = pow(2.0, _scale->value()); - - for (int i = 0; i < innerHeight; i += HEIGHTFIELD_BLOCK_SIZE) { - for (int j = 0; j < innerWidth; j += HEIGHTFIELD_BLOCK_SIZE) { - Heightfield* heightfield = new Heightfield(); - - int extendedHeightSize = HEIGHTFIELD_BLOCK_SIZE + HeightfieldHeight::HEIGHT_EXTENSION; - QVector heightContents(extendedHeightSize * extendedHeightSize); - quint16* dest = heightContents.data(); - const quint16* src = contents.constData() + i * width + j; - int copyWidth = qMin(width - j, extendedHeightSize); - int copyHeight = qMin(height - i, extendedHeightSize); - for (int z = 0; z < copyHeight; z++, src += width, dest += extendedHeightSize) { - memcpy(dest, src, copyWidth * sizeof(quint16)); - } - heightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(extendedHeightSize, heightContents))); - - int materialWidth = HEIGHTFIELD_BLOCK_SIZE + HeightfieldData::SHARED_EDGE; - int materialHeight = materialWidth; - if (_color->getColor()) { - int colorWidth = _color->getColor()->getWidth(); - const QByteArray& contents = _color->getColor()->getContents(); - int colorHeight = contents.size() / (colorWidth * DataBlock::COLOR_BYTES); - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - materialWidth = HEIGHTFIELD_BLOCK_SIZE * innerColorWidth / innerWidth + HeightfieldData::SHARED_EDGE; - materialHeight = HEIGHTFIELD_BLOCK_SIZE * innerColorHeight / innerHeight + HeightfieldData::SHARED_EDGE; - QByteArray colorContents(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0); - int colorI = i * (materialWidth - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; - int colorJ = j * (materialHeight - HeightfieldData::SHARED_EDGE) / HEIGHTFIELD_BLOCK_SIZE; - char* dest = colorContents.data(); - const char* src = contents.constData() + (colorI * colorWidth + colorJ) * DataBlock::COLOR_BYTES; - int copyWidth = qMin(colorWidth - colorJ, materialWidth); - int copyHeight = qMin(colorHeight - colorI, materialHeight); - for (int z = 0; z < copyHeight; z++, src += colorWidth * DataBlock::COLOR_BYTES, - dest += materialWidth * DataBlock::COLOR_BYTES) { - memcpy(dest, src, copyWidth * DataBlock::COLOR_BYTES); - } - heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, colorContents))); - - } else { - heightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(materialWidth, - QByteArray(materialWidth * materialHeight * DataBlock::COLOR_BYTES, 0xFF)))); - } - heightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, - QByteArray(materialWidth * materialHeight, 0), QVector()))); - - heightfield->setScale(scale); - heightfield->setAspectY(_heightScale->value() / scale); - heightfield->setTranslation(_translation->getValue() + glm::vec3((j / HEIGHTFIELD_BLOCK_SIZE) * scale, - _heightOffset->value(), (i / HEIGHTFIELD_BLOCK_SIZE) * scale)); - - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, heightfield)) }; - Application::getInstance()->getMetavoxels()->applyEdit(message, true); - } - } + MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, _spanner)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); } +const int HEIGHTFIELD_BLOCK_SIZE = 256; + void ImportHeightfieldTool::updateSpanner() { Heightfield* heightfield = static_cast(_spanner.data()); heightfield->setHeight(_height->getHeight()); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index a57cc8a31c..07eb2d02a3 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1490,6 +1490,151 @@ void HeightfieldNode::clearChildren() { } void HeightfieldNode::mergeChildren() { + if (isLeaf()) { + return; + } + int heightWidth = 0; + int heightHeight = 0; + int colorWidth = 0; + int colorHeight = 0; + int materialWidth = 0; + int materialHeight = 0; + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldHeightPointer childHeight = _children[i]->getHeight(); + if (childHeight) { + int childHeightWidth = childHeight->getWidth(); + int childHeightHeight = childHeight->getContents().size() / childHeightWidth; + heightWidth = qMax(heightWidth, childHeightWidth); + heightHeight = qMax(heightHeight, childHeightHeight); + } + HeightfieldColorPointer childColor = _children[i]->getColor(); + if (childColor) { + int childColorWidth = childColor->getWidth(); + int childColorHeight = childColor->getContents().size() / (childColorWidth * DataBlock::COLOR_BYTES); + colorWidth = qMax(colorWidth, childColorWidth); + colorHeight = qMax(colorHeight, childColorHeight); + } + HeightfieldMaterialPointer childMaterial = _children[i]->getMaterial(); + if (childMaterial) { + int childMaterialWidth = childMaterial->getWidth(); + int childMaterialHeight = childMaterial->getContents().size() / childMaterialWidth; + materialWidth = qMax(materialWidth, childMaterialWidth); + materialHeight = qMax(materialHeight, childMaterialHeight); + } + } + if (heightWidth > 0) { + QVector heightContents(heightWidth * heightHeight); + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldHeightPointer childHeight = _children[i]->getHeight(); + int childHeightWidth = childHeight->getWidth(); + int childHeightHeight = childHeight->getContents().size() / childHeightWidth; + if (childHeightWidth != heightWidth || childHeightHeight != heightHeight) { + qWarning() << "Height dimension mismatch [heightWidth=" << heightWidth << ", heightHeight=" << heightHeight << + ", childHeightWidth=" << childHeightWidth << ", childHeightHeight=" << childHeightHeight << "]"; + continue; + } + int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int innerQuadrantHeightWidth = innerHeightWidth / 2; + int innerQuadrantHeightHeight = innerHeightHeight / 2; + int quadrantHeightWidth = innerQuadrantHeightWidth + HeightfieldHeight::HEIGHT_EXTENSION - 1; + int quadrantHeightHeight = innerQuadrantHeightHeight + HeightfieldHeight::HEIGHT_EXTENSION - 1; + quint16* dest = heightContents.data() + (i & Y_MAXIMUM_FLAG ? (innerQuadrantHeightHeight + 1) * heightWidth : 0) + + (i & X_MAXIMUM_FLAG ? innerQuadrantHeightWidth + 1 : 0); + const quint16* src = childHeight->getContents().constData(); + for (int z = 0; z < quadrantHeightHeight; z++, dest += heightWidth, src += heightWidth * 2) { + const quint16* lineSrc = src; + for (quint16* lineDest = dest, *end = dest + quadrantHeightWidth; lineDest != end; lineDest++, lineSrc += 2) { + *lineDest = *lineSrc; + } + } + } + _height = new HeightfieldHeight(heightWidth, heightContents); + + } else { + _height.reset(); + } + if (colorWidth > 0) { + QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0); + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldColorPointer childColor = _children[i]->getColor(); + 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 char* src = childColor->getContents().constData(); + for (int z = 0; z < quadrantColorHeight; z++, dest += colorWidth * DataBlock::COLOR_BYTES, + src += colorWidth * DataBlock::COLOR_BYTES * 2) { + const char* 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(); + 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(); + } } Heightfield::Heightfield() : From f6a6e185cfae5691c4300b8101d644cdae1d4eb2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Nov 2014 18:53:27 -0800 Subject: [PATCH 07/29] Heightfield import simplifications. --- interface/src/ui/MetavoxelEditor.cpp | 37 ++++++++++------------------ interface/src/ui/MetavoxelEditor.h | 4 +-- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index cf28a79995..65faa938e2 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -732,11 +732,11 @@ HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) : layout()->addWidget(widget); _form->addRow("Translation:", _translation = new Vec3Editor(widget)); - _form->addRow("Scale:", _scale = new QDoubleSpinBox()); - _scale->setMinimum(-FLT_MAX); - _scale->setMaximum(FLT_MAX); - _scale->setPrefix("2^"); - _scale->setValue(2.0); + _form->addRow("Spacing:", _spacing = new QDoubleSpinBox()); + _spacing->setMaximum(FLT_MAX); + _spacing->setDecimals(3); + _spacing->setSingleStep(0.001); + _spacing->setValue(1.0); QPushButton* applyButton = new QPushButton("Apply"); layout()->addWidget(applyButton); @@ -747,28 +747,20 @@ bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const { return attribute->inherits("SpannerSetAttribute"); } -void HeightfieldTool::render() { - float scale = pow(2.0, _scale->value()); - _translation->setSingleStep(scale); - glm::vec3 quantizedTranslation = scale * glm::floor(_translation->getValue() / scale); - _translation->setValue(quantizedTranslation); -} - ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Import Heightfield"), _spanner(new Heightfield()) { _form->addRow("Height Scale:", _heightScale = new QDoubleSpinBox()); - const double MAX_OFFSET_SCALE = 100000.0; - _heightScale->setMaximum(MAX_OFFSET_SCALE); + _heightScale->setMaximum(FLT_MAX); _heightScale->setSingleStep(0.01); - _heightScale->setValue(8.0); + _heightScale->setValue(16.0); connect(_heightScale, static_cast(&QDoubleSpinBox::valueChanged), this, &ImportHeightfieldTool::updateSpanner); _form->addRow("Height Offset:", _heightOffset = new QDoubleSpinBox()); - _heightOffset->setMinimum(-MAX_OFFSET_SCALE); - _heightOffset->setMaximum(MAX_OFFSET_SCALE); + _heightOffset->setMinimum(-FLT_MAX); + _heightOffset->setMaximum(FLT_MAX); _heightOffset->setSingleStep(0.01); connect(_heightOffset, static_cast(&QDoubleSpinBox::valueChanged), this, &ImportHeightfieldTool::updateSpanner); @@ -780,7 +772,7 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : connect(_color, &HeightfieldColorEditor::colorChanged, this, &ImportHeightfieldTool::updateSpanner); connect(_translation, &Vec3Editor::valueChanged, this, &ImportHeightfieldTool::updateSpanner); - connect(_scale, static_cast(&QDoubleSpinBox::valueChanged), this, + connect(_spacing, static_cast(&QDoubleSpinBox::valueChanged), this, &ImportHeightfieldTool::updateSpanner); } @@ -801,22 +793,19 @@ void ImportHeightfieldTool::apply() { Application::getInstance()->getMetavoxels()->applyEdit(message, true); } -const int HEIGHTFIELD_BLOCK_SIZE = 256; - void ImportHeightfieldTool::updateSpanner() { Heightfield* heightfield = static_cast(_spanner.data()); heightfield->setHeight(_height->getHeight()); heightfield->setColor(_color->getColor()); - float scale = pow(2.0, _scale->value()); + float scale = 1.0f; float aspectZ = 1.0f; if (_height->getHeight()) { int width = _height->getHeight()->getWidth(); int innerWidth = width - HeightfieldHeight::HEIGHT_EXTENSION; int innerHeight = _height->getHeight()->getContents().size() / width - HeightfieldHeight::HEIGHT_EXTENSION; - float widthBlocks = glm::ceil((float)innerWidth / HEIGHTFIELD_BLOCK_SIZE); - scale *= widthBlocks; - aspectZ = glm::ceil((float)innerHeight / HEIGHTFIELD_BLOCK_SIZE) / widthBlocks; + scale = innerWidth * _spacing->value(); + aspectZ = (float)innerHeight / innerWidth; } heightfield->setScale(scale); heightfield->setAspectY(_heightScale->value() / scale); diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index cccb41ecfc..4e870a9982 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -260,8 +260,6 @@ public: virtual bool appliesTo(const AttributePointer& attribute) const; - virtual void render(); - protected slots: virtual void apply() = 0; @@ -270,7 +268,7 @@ protected: QFormLayout* _form; Vec3Editor* _translation; - QDoubleSpinBox* _scale; + QDoubleSpinBox* _spacing; }; /// Allows importing a heightfield. From 790b085307ae8db752af6031b661cc0e0586b7fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Nov 2014 13:02:11 -0800 Subject: [PATCH 08/29] Rejiggering the heightfield renderer for LOD. --- interface/src/MetavoxelSystem.cpp | 237 ++++++++++++++------------- interface/src/MetavoxelSystem.h | 34 +++- libraries/metavoxels/src/Spanner.cpp | 24 ++- libraries/metavoxels/src/Spanner.h | 3 + 4 files changed, 174 insertions(+), 124 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b98fea8eca..b8d1761297 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2073,57 +2073,67 @@ void StaticModelRenderer::applyURL(const QUrl& url) { } HeightfieldRenderer::HeightfieldRenderer() { - glGenTextures(1, &_heightTextureID); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - 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); - - glGenTextures(1, &_colorTextureID); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glGenTextures(1, &_materialTextureID); - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - 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); - - glBindTexture(GL_TEXTURE_2D, 0); -} - -HeightfieldRenderer::~HeightfieldRenderer() { - glDeleteTextures(1, &_heightTextureID); - glDeleteTextures(1, &_colorTextureID); - glDeleteTextures(1, &_materialTextureID); } void HeightfieldRenderer::init(Spanner* spanner) { SpannerRenderer::init(spanner); Heightfield* heightfield = static_cast(spanner); - applyHeight(heightfield->getHeight()); - applyColor(heightfield->getColor()); - applyMaterial(heightfield->getMaterial()); - - connect(heightfield, &Heightfield::heightChanged, this, &HeightfieldRenderer::applyHeight); - connect(heightfield, &Heightfield::colorChanged, this, &HeightfieldRenderer::applyColor); - connect(heightfield, &Heightfield::materialChanged, this, &HeightfieldRenderer::applyMaterial); + connect(heightfield, &Heightfield::rootChanged, this, &HeightfieldRenderer::updateRoot); + updateRoot(); } void HeightfieldRenderer::render(bool cursor) { - // create the buffer objects lazily Heightfield* heightfield = static_cast(_spanner); - if (!heightfield->getHeight()) { + _root->render(heightfield->getTranslation(), heightfield->getRotation(), glm::vec3(heightfield->getScale(), + heightfield->getScale() * heightfield->getAspectY(), heightfield->getScale() * heightfield->getAspectZ()), cursor); +} + +void HeightfieldRenderer::updateRoot() { + Heightfield* heightfield = static_cast(_spanner); + _root = new HeightfieldRendererNode(heightfield->getRoot()); +} + +HeightfieldRendererNode::HeightfieldRendererNode(const HeightfieldNodePointer& heightfieldNode) : + _heightfieldNode(heightfieldNode), + _heightTextureID(0), + _colorTextureID(0), + _materialTextureID(0) { + + for (int i = 0; i < CHILD_COUNT; i++) { + HeightfieldNodePointer child = heightfieldNode->getChild(i); + if (child) { + _children[i] = new HeightfieldRendererNode(child); + } + } +} + +HeightfieldRendererNode::~HeightfieldRendererNode() { + glDeleteTextures(1, &_heightTextureID); + glDeleteTextures(1, &_colorTextureID); + glDeleteTextures(1, &_materialTextureID); +} + +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; + +void HeightfieldRendererNode::render(const glm::vec3& translation, const glm::quat& rotation, + const glm::vec3& scale, bool cursor) { + if (!isLeaf()) { + glm::vec3 nextScale(scale.x * 0.5f, scale.y, scale.z * 0.5f); + glm::vec3 xOffset = rotation * glm::vec3(nextScale.x, 0.0f, 0.0f); + glm::vec3 zOffset = rotation * glm::vec3(0.0f, 0.0f, nextScale.z); + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->render(translation + (i & X_MAXIMUM_FLAG ? xOffset : glm::vec3()) + + (i & Y_MAXIMUM_FLAG ? zOffset : glm::vec3()), rotation, nextScale, cursor); + } return; } - int width = heightfield->getHeight()->getWidth(); - int height = heightfield->getHeight()->getContents().size() / width; - + if (!_heightfieldNode->getHeight()) { + return; + } + int width = _heightfieldNode->getHeight()->getWidth(); + int height = _heightfieldNode->getHeight()->getContents().size() / width; int innerWidth = width - 2 * HeightfieldHeight::HEIGHT_BORDER; int innerHeight = height - 2 * HeightfieldHeight::HEIGHT_BORDER; int vertexCount = width * height; @@ -2180,17 +2190,71 @@ void HeightfieldRenderer::render(bool cursor) { bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int)); bufferPair.second.release(); } + if (_heightTextureID == 0) { + glGenTextures(1, &_heightTextureID); + glBindTexture(GL_TEXTURE_2D, _heightTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + 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); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + const QVector& heightContents = _heightfieldNode->getHeight()->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, width, height, 0, + GL_RED, GL_UNSIGNED_SHORT, heightContents.constData()); + + glGenTextures(1, &_colorTextureID); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (_heightfieldNode->getColor()) { + const QByteArray& contents = _heightfieldNode->getColor()->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, _heightfieldNode->getColor()->getWidth(), + contents.size() / (_heightfieldNode->getColor()->getWidth() * DataBlock::COLOR_BYTES), + 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); + + } else { + const quint8 WHITE_COLOR[] = { 255, 255, 255 }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); + } + + glGenTextures(1, &_materialTextureID); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + 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); + if (_heightfieldNode->getMaterial()) { + const QByteArray& contents = _heightfieldNode->getMaterial()->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, _heightfieldNode->getMaterial()->getWidth(), + contents.size() / _heightfieldNode->getMaterial()->getWidth(), + 0, GL_RED, GL_UNSIGNED_BYTE, contents.constData()); + + const QVector& materials = _heightfieldNode->getMaterial()->getMaterials(); + _networkTextures.resize(materials.size()); + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& material = materials.at(i); + if (material) { + _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( + static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); + } + } + } else { + const quint8 ZERO_VALUE = 0; + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &ZERO_VALUE); + } + glBindTexture(GL_TEXTURE_2D, 0); + } - float xScale = heightfield->getScale(), zScale = xScale * heightfield->getAspectZ(); if (cursor) { bufferPair.first.bind(); bufferPair.second.bind(); glPushMatrix(); - glTranslatef(heightfield->getTranslation().x, heightfield->getTranslation().y, heightfield->getTranslation().z); - glm::vec3 axis = glm::axis(heightfield->getRotation()); - glRotatef(glm::degrees(glm::angle(heightfield->getRotation())), axis.x, axis.y, axis.z); - glScalef(xScale, xScale * heightfield->getAspectY(), zScale); + 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); @@ -2208,13 +2272,12 @@ void HeightfieldRenderer::render(bool cursor) { bufferPair.second.release(); return; } - HeightfieldBaseLayerBatch baseBatch; baseBatch.vertexBuffer = &bufferPair.first; baseBatch.indexBuffer = &bufferPair.second; - baseBatch.translation = heightfield->getTranslation(); - baseBatch.rotation = heightfield->getRotation(); - baseBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + baseBatch.translation = translation; + baseBatch.rotation = rotation; + baseBatch.scale = scale; baseBatch.vertexCount = vertexCount; baseBatch.indexCount = indexCount; baseBatch.heightTextureID = _heightTextureID; @@ -2223,13 +2286,13 @@ void HeightfieldRenderer::render(bool cursor) { baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); - if (heightfield->getMaterial() && !_networkTextures.isEmpty()) { + if (!_networkTextures.isEmpty()) { HeightfieldSplatBatch splatBatch; splatBatch.vertexBuffer = &bufferPair.first; splatBatch.indexBuffer = &bufferPair.second; - splatBatch.translation = heightfield->getTranslation(); - splatBatch.rotation = heightfield->getRotation(); - splatBatch.scale = glm::vec3(xScale, xScale * heightfield->getAspectY(), zScale); + splatBatch.translation = translation; + splatBatch.rotation = rotation; + splatBatch.scale = scale; splatBatch.vertexCount = vertexCount; splatBatch.indexCount = indexCount; splatBatch.heightTextureID = _heightTextureID; @@ -2237,10 +2300,10 @@ void HeightfieldRenderer::render(bool cursor) { splatBatch.materialTextureID = _materialTextureID; splatBatch.textureScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); splatBatch.splatTextureOffset = glm::vec2( - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(1.0f, 0.0f, 0.0f)) / xScale, - glm::dot(heightfield->getTranslation(), heightfield->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) / zScale); + glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, + glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); - const QVector& materials = heightfield->getMaterial()->getMaterials(); + const QVector& materials = _heightfieldNode->getMaterial()->getMaterials(); for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { for (int j = 0; j < SPLAT_COUNT; j++) { int index = i + j; @@ -2248,8 +2311,8 @@ void HeightfieldRenderer::render(bool cursor) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast(materials.at(index).data()); - splatBatch.splatTextureScalesS[j] = xScale / material->getScaleS(); - splatBatch.splatTextureScalesT[j] = zScale / material->getScaleT(); + splatBatch.splatTextureScalesS[j] = scale.x / material->getScaleS(); + splatBatch.splatTextureScalesT[j] = scale.z / material->getScaleT(); splatBatch.splatTextureIDs[j] = texture->getID(); } else { @@ -2265,62 +2328,14 @@ void HeightfieldRenderer::render(bool cursor) { } } -void HeightfieldRenderer::applyHeight(const HeightfieldHeightPointer& height) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, _heightTextureID); - if (height) { - const QVector& contents = height->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, height->getWidth(), contents.size() / height->getWidth(), 0, - GL_RED, GL_UNSIGNED_SHORT, contents.constData()); - - } else { - const quint16 ZERO_VALUE = 0; - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, 1, 1, 0, GL_RED, GL_UNSIGNED_SHORT, &ZERO_VALUE); - } - glBindTexture(GL_TEXTURE_2D, 0); -} - -void HeightfieldRenderer::applyColor(const HeightfieldColorPointer& color) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - if (color) { - const QByteArray& contents = color->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, color->getWidth(), - contents.size() / (color->getWidth() * DataBlock::COLOR_BYTES), 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); - - } else { - const quint8 WHITE_COLOR[] = { 255, 255, 255 }; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); - } - glBindTexture(GL_TEXTURE_2D, 0); -} - -void HeightfieldRenderer::applyMaterial(const HeightfieldMaterialPointer& material) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, _materialTextureID); - if (material) { - const QByteArray& contents = material->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, material->getWidth(), contents.size() / material->getWidth(), 0, - GL_RED, GL_UNSIGNED_BYTE, contents.constData()); - - const QVector& materials = material->getMaterials(); - _networkTextures.resize(materials.size()); - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& material = materials.at(i); - if (material) { - _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( - static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); - } else { - _networkTextures[i].clear(); - } +bool HeightfieldRendererNode::isLeaf() const { + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i]) { + return false; } - } else { - const quint8 ZERO_VALUE = 0; - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &ZERO_VALUE); - _networkTextures.clear(); } - glBindTexture(GL_TEXTURE_2D, 0); + return true; } -QHash HeightfieldRenderer::_bufferPairs; +QHash HeightfieldRendererNode::_bufferPairs; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index c1cdfd3624..d7173e3cf0 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -24,6 +24,7 @@ #include "renderer/ProgramObject.h" class HeightfieldBaseLayerBatch; +class HeightfieldRendererNode; class HeightfieldSplatBatch; class Model; class VoxelBatch; @@ -419,6 +420,8 @@ private: Model* _model; }; +typedef QExplicitlySharedDataPointer HeightfieldRendererNodePointer; + /// Renders heightfields. class HeightfieldRenderer : public SpannerRenderer { Q_OBJECT @@ -426,26 +429,45 @@ class HeightfieldRenderer : public SpannerRenderer { public: Q_INVOKABLE HeightfieldRenderer(); - virtual ~HeightfieldRenderer(); virtual void init(Spanner* spanner); virtual void render(bool cursor = false); private slots: - void applyHeight(const HeightfieldHeightPointer& height); - void applyColor(const HeightfieldColorPointer& color); - void applyMaterial(const HeightfieldMaterialPointer& material); + void updateRoot(); private: + HeightfieldRendererNodePointer _root; +}; + +/// A node in the heightfield renderer quadtree. +class HeightfieldRendererNode : public QSharedData { +public: + + static const int CHILD_COUNT = 4; + + HeightfieldRendererNode(const HeightfieldNodePointer& heightfieldNode); + virtual ~HeightfieldRendererNode(); + + void render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor = false); + +private: + + bool isLeaf() const; + + HeightfieldNodePointer _heightfieldNode; + + HeightfieldRendererNodePointer _children[CHILD_COUNT]; + GLuint _heightTextureID; GLuint _colorTextureID; GLuint _materialTextureID; QVector _networkTextures; - typedef QPair IntPair; - typedef QPair BufferPair; + typedef QPair IntPair; + typedef QPair BufferPair; static QHash _bufferPairs; }; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 07eb2d02a3..ce8aa7c928 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1684,6 +1684,12 @@ void Heightfield::setMaterial(const HeightfieldMaterialPointer& material) { } } +void Heightfield::setRoot(const HeightfieldNodePointer& root) { + if (_root != root) { + emit rootChanged(_root = root); + } +} + bool Heightfield::isHeightfield() const { return true; } @@ -2667,8 +2673,10 @@ void Heightfield::readExtra(Bitstream& in) { } HeightfieldStreamBase base = { in, lod, lod }; HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - _root = new HeightfieldNode(); - _root->read(state); + + HeightfieldNodePointer root(new HeightfieldNode()); + root->read(state); + setRoot(root); } void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { @@ -2705,11 +2713,12 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { bool changed; in >> changed; if (changed) { - _root = new HeightfieldNode(); - _root->readDelta(static_cast(reference)->getRoot(), state); + HeightfieldNodePointer root(new HeightfieldNode()); + root->readDelta(static_cast(reference)->getRoot(), state); + setRoot(root); } else if (state.becameSubdividedOrCollapsed()) { - _root = _root->readSubdivision(state); + setRoot(HeightfieldNodePointer(_root->readSubdivision(state))); } } @@ -2724,10 +2733,11 @@ void Heightfield::updateBounds() { } void Heightfield::updateRoot() { - _root = new HeightfieldNode(); + HeightfieldNodePointer root(new HeightfieldNode()); if (_height) { - _root->setContents(_height, _color, _material); + root->setContents(_height, _color, _material); } + setRoot(root); } MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index d67c23909d..e9ee43897a 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -540,6 +540,7 @@ 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) public: @@ -560,6 +561,7 @@ public: void setMaterial(const HeightfieldMaterialPointer& material); const HeightfieldMaterialPointer& getMaterial() const { return _material; } + void setRoot(const HeightfieldNodePointer& root); const HeightfieldNodePointer& getRoot() const { return _root; } virtual bool isHeightfield() const; @@ -596,6 +598,7 @@ signals: void heightChanged(const HeightfieldHeightPointer& height); void colorChanged(const HeightfieldColorPointer& color); void materialChanged(const HeightfieldMaterialPointer& material); + void rootChanged(const HeightfieldNodePointer& root); protected: From 0a329bbe3c35c9bb4e151700e5d3ace993a46f86 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Nov 2014 16:18:57 -0800 Subject: [PATCH 09/29] More work on LOD rendering; now rendering in preview. --- interface/src/MetavoxelSystem.cpp | 130 +++++++++++++++------------ interface/src/MetavoxelSystem.h | 11 +-- interface/src/ui/MetavoxelEditor.cpp | 4 +- libraries/metavoxels/src/Spanner.cpp | 24 ++--- libraries/metavoxels/src/Spanner.h | 6 +- 5 files changed, 94 insertions(+), 81 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b8d1761297..a4d8c6ff37 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -550,10 +550,47 @@ void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const applyMaterialEdit(edit, true); } -class SpannerCursorRenderVisitor : public SpannerVisitor { +class SpannerRenderVisitor : public SpannerVisitor { public: - SpannerCursorRenderVisitor(const Box& bounds); + SpannerRenderVisitor(const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info); + virtual bool visit(Spanner* spanner); + +protected: + + int _containmentDepth; +}; + +SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : + SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), + QVector(), QVector(), lod, + encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), + _containmentDepth(INT_MAX) { +} + +int SpannerRenderVisitor::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; + } + return SpannerVisitor::visit(info); +} + +bool SpannerRenderVisitor::visit(Spanner* spanner) { + spanner->getRenderer()->render(_lod, _containmentDepth <= _depth); + return true; +} + +class SpannerCursorRenderVisitor : public SpannerRenderVisitor { +public: + + SpannerCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds); virtual bool visit(Spanner* spanner); @@ -564,20 +601,20 @@ private: Box _bounds; }; -SpannerCursorRenderVisitor::SpannerCursorRenderVisitor(const Box& bounds) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute()), +SpannerCursorRenderVisitor::SpannerCursorRenderVisitor(const MetavoxelLOD& lod, const Box& bounds) : + SpannerRenderVisitor(lod), _bounds(bounds) { } bool SpannerCursorRenderVisitor::visit(Spanner* spanner) { if (spanner->isHeightfield()) { - spanner->getRenderer()->render(true); + spanner->getRenderer()->render(_lod, _containmentDepth <= _depth, true); } return true; } int SpannerCursorRenderVisitor::visit(MetavoxelInfo& info) { - return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION; + return info.getBounds().intersects(_bounds) ? SpannerRenderVisitor::visit(info) : STOP_RECURSION; } void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float radius) { @@ -604,7 +641,7 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r glActiveTexture(GL_TEXTURE0); glm::vec3 extents(radius, radius, radius); - SpannerCursorRenderVisitor visitor(Box(position - extents, position + extents)); + SpannerCursorRenderVisitor visitor(getLOD(), Box(position - extents, position + extents)); guide(visitor); _heightfieldCursorProgram.release(); @@ -678,7 +715,7 @@ void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) _heightfieldCursorProgram.bind(); - SpannerCursorRenderVisitor spannerVisitor(bounds); + SpannerCursorRenderVisitor spannerVisitor(getLOD(), bounds); guide(spannerVisitor); _heightfieldCursorProgram.release(); @@ -1880,43 +1917,6 @@ void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float data.guide(spannerSimulateVisitor); } -class SpannerRenderVisitor : public SpannerVisitor { -public: - - SpannerRenderVisitor(const MetavoxelLOD& lod); - - virtual int visit(MetavoxelInfo& info); - virtual bool visit(Spanner* spanner); - -private: - - int _containmentDepth; -}; - -SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : - SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), - QVector(), QVector(), lod, - encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), - _containmentDepth(INT_MAX) { -} - -int SpannerRenderVisitor::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; - } - return SpannerVisitor::visit(info); -} - -bool SpannerRenderVisitor::visit(Spanner* spanner) { - spanner->getRenderer()->render(); - return true; -} - class BufferRenderVisitor : public MetavoxelVisitor { public: @@ -1970,7 +1970,7 @@ SphereRenderer::SphereRenderer() { } -void SphereRenderer::render(bool cursor) { +void SphereRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Sphere* sphere = static_cast(_spanner); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); @@ -1990,7 +1990,7 @@ void SphereRenderer::render(bool cursor) { CuboidRenderer::CuboidRenderer() { } -void CuboidRenderer::render(bool cursor) { +void CuboidRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Cuboid* cuboid = static_cast(_spanner); const QColor& color = cuboid->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); @@ -2041,7 +2041,7 @@ void StaticModelRenderer::simulate(float deltaTime) { _model->simulate(deltaTime); } -void StaticModelRenderer::render(bool cursor) { +void StaticModelRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { _model->render(); } @@ -2083,10 +2083,9 @@ void HeightfieldRenderer::init(Spanner* spanner) { updateRoot(); } -void HeightfieldRenderer::render(bool cursor) { +void HeightfieldRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Heightfield* heightfield = static_cast(_spanner); - _root->render(heightfield->getTranslation(), heightfield->getRotation(), glm::vec3(heightfield->getScale(), - heightfield->getScale() * heightfield->getAspectY(), heightfield->getScale() * heightfield->getAspectZ()), cursor); + _root->render(heightfield, heightfield->transformLOD(lod), glm::vec2(), 1.0f, contained, cursor); } void HeightfieldRenderer::updateRoot() { @@ -2117,15 +2116,28 @@ HeightfieldRendererNode::~HeightfieldRendererNode() { const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; -void HeightfieldRendererNode::render(const glm::vec3& translation, const glm::quat& rotation, - const glm::vec3& scale, bool cursor) { - if (!isLeaf()) { - glm::vec3 nextScale(scale.x * 0.5f, scale.y, scale.z * 0.5f); - glm::vec3 xOffset = rotation * glm::vec3(nextScale.x, 0.0f, 0.0f); - glm::vec3 zOffset = rotation * glm::vec3(0.0f, 0.0f, nextScale.z); +void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLOD& lod, + const glm::vec2& minimum, float size, bool contained, bool cursor) { + const glm::quat& rotation = heightfield->getRotation(); + glm::vec3 scale(heightfield->getScale() * size, heightfield->getScale() * heightfield->getAspectY(), + heightfield->getScale() * heightfield->getAspectZ() * size); + glm::vec3 translation = heightfield->getTranslation() + rotation * glm::vec3(minimum.x * heightfield->getScale(), + 0.0f, minimum.y * heightfield->getScale() * heightfield->getAspectZ()); + if (!contained) { + Frustum::IntersectionType type = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( + glm::translate(translation) * glm::mat4_cast(rotation) * Box(glm::vec3(), scale)); + if (type == Frustum::NO_INTERSECTION) { + return; + } + if (type == Frustum::CONTAINS_INTERSECTION) { + contained = true; + } + } + if (!isLeaf() && lod.shouldSubdivide(minimum, size)) { + float nextSize = size * 0.5f; for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->render(translation + (i & X_MAXIMUM_FLAG ? xOffset : glm::vec3()) + - (i & Y_MAXIMUM_FLAG ? zOffset : glm::vec3()), rotation, nextScale, cursor); + _children[i]->render(heightfield, lod, minimum + glm::vec2(i & X_MAXIMUM_FLAG ? nextSize : 0.0f, + i & Y_MAXIMUM_FLAG ? nextSize : 0.0f), nextSize, contained, cursor); } return; } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index d7173e3cf0..059ef27de4 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -381,7 +381,7 @@ public: Q_INVOKABLE SphereRenderer(); - virtual void render(bool cursor = false); + virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); }; /// Renders cuboids. @@ -392,7 +392,7 @@ public: Q_INVOKABLE CuboidRenderer(); - virtual void render(bool cursor = false); + virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); }; /// Renders static models. @@ -405,7 +405,7 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(bool cursor = false); + virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; private slots: @@ -431,7 +431,7 @@ public: Q_INVOKABLE HeightfieldRenderer(); virtual void init(Spanner* spanner); - virtual void render(bool cursor = false); + virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); private slots: @@ -451,7 +451,8 @@ public: HeightfieldRendererNode(const HeightfieldNodePointer& heightfieldNode); virtual ~HeightfieldRendererNode(); - void render(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor = false); + void render(Heightfield* heightfield, const MetavoxelLOD& lod, const glm::vec2& minimum, float size, + bool contained, bool cursor = false); private: diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 65faa938e2..6d1e181907 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -634,7 +634,7 @@ void PlaceSpannerTool::simulate(float deltaTime) { void PlaceSpannerTool::renderPreview() { Spanner* spanner = static_cast(getSpanner().data()); - spanner->getRenderer()->render(); + spanner->getRenderer()->render(Application::getInstance()->getMetavoxels()->getLOD()); } bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const { @@ -781,7 +781,7 @@ void ImportHeightfieldTool::simulate(float deltaTime) { } void ImportHeightfieldTool::renderPreview() { - static_cast(_spanner.data())->getRenderer()->render(); + static_cast(_spanner.data())->getRenderer()->render(Application::getInstance()->getMetavoxels()->getLOD()); } void ImportHeightfieldTool::apply() { diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index ce8aa7c928..608dbc77c2 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -168,7 +168,7 @@ void SpannerRenderer::simulate(float deltaTime) { // nothing by default } -void SpannerRenderer::render(bool cursor) { +void SpannerRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { // nothing by default } @@ -1573,10 +1573,10 @@ void HeightfieldNode::mergeChildren() { 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 char* src = childColor->getContents().constData(); + 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 char* lineSrc = src; + 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]; @@ -1690,6 +1690,15 @@ void Heightfield::setRoot(const HeightfieldNodePointer& root) { } } +MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { + // after transforming into unit space, we scale the threshold in proportion to vertical distance + glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); + glm::vec3 position = glm::inverse(getRotation()) * (lod.position - getTranslation()) * inverseScale; + const float THRESHOLD_MULTIPLIER = 256.0f; + return MetavoxelLOD(glm::vec3(position.x, position.z, 0.0f), lod.threshold * + qMax(0.5f, glm::abs(position.y * _aspectY - 0.5f)) * THRESHOLD_MULTIPLIER); +} + bool Heightfield::isHeightfield() const { return true; } @@ -2739,12 +2748,3 @@ void Heightfield::updateRoot() { } setRoot(root); } - -MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { - // after transforming into unit space, we scale the threshold in proportion to vertical distance - glm::vec3 inverseScale(1.0f / getScale(), 1.0f / (getScale() * _aspectY), 1.0f / (getScale() * _aspectZ)); - glm::vec3 position = glm::inverse(getRotation()) * (lod.position - getTranslation()) * inverseScale; - const float THRESHOLD_MULTIPLIER = 2.0f; - return MetavoxelLOD(glm::vec3(position.x, position.z, 0.0f), lod.threshold * - qMax(0.5f, glm::abs(position.y - 0.5f)) * THRESHOLD_MULTIPLIER); -} diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index e9ee43897a..0b24c7e318 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -134,7 +134,7 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual void render(bool cursor = false); + virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; protected: @@ -564,6 +564,8 @@ public: void setRoot(const HeightfieldNodePointer& root); const HeightfieldNodePointer& getRoot() const { return _root; } + MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; + virtual bool isHeightfield() const; virtual float getHeight(const glm::vec3& location) const; @@ -611,8 +613,6 @@ private slots: private: - MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; - float _aspectY; float _aspectZ; From 6be18109679874eeb5b0f2a765cca786203c54ba Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Nov 2014 17:57:03 -0800 Subject: [PATCH 10/29] More work on subdivision. --- libraries/metavoxels/src/SharedObject.cpp | 8 ++++++ libraries/metavoxels/src/SharedObject.h | 6 +++++ libraries/metavoxels/src/Spanner.cpp | 31 +++++++++++++++++++++++ libraries/metavoxels/src/Spanner.h | 2 ++ 4 files changed, 47 insertions(+) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index e57b7d9a8e..8f57c8be34 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -147,6 +147,14 @@ void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) // nothing by default } +void SharedObject::writeExtraSubdivision(Bitstream& out) { + // nothing by default +} + +void SharedObject::readExtraSubdivision(Bitstream& in) { + // nothing by default +} + QAtomicInt SharedObject::_nextID(1); WeakSharedObjectHash SharedObject::_weakHash; QReadWriteLock SharedObject::_weakHashLock; diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 7f44ffec82..f35fde802c 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -92,6 +92,12 @@ public: /// Reads the delta-encoded non-property contents of this object from the specified stream. virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + /// Writes the subdivision of the non-property contents of this object to the specified stream. + virtual void writeExtraSubdivision(Bitstream& out); + + /// Reads the subdivision of the non-property contents of this object from the specified stream. + virtual void readExtraSubdivision(Bitstream& in); + private: int _id; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 608dbc77c2..bdccb18b7e 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2731,6 +2731,37 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { } } +void Heightfield::writeExtraSubdivision(Bitstream& out) { + MetavoxelLOD lod, referenceLOD; + if (out.getContext()) { + MetavoxelStreamBase* base = static_cast(out.getContext()); + lod = transformLOD(base->lod); + referenceLOD = transformLOD(base->referenceLOD); + } + HeightfieldStreamBase base = { out, lod, referenceLOD }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + + if (state.becameSubdivided()) { + out << SharedObjectPointer(this); + _root->writeSubdivision(state); + } +} + +void Heightfield::readExtraSubdivision(Bitstream& in) { + MetavoxelLOD lod, referenceLOD; + if (in.getContext()) { + MetavoxelStreamBase* base = static_cast(in.getContext()); + lod = transformLOD(base->lod); + referenceLOD = transformLOD(base->referenceLOD); + } + HeightfieldStreamBase base = { in, lod, referenceLOD }; + HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; + + if (state.becameSubdividedOrCollapsed()) { + setRoot(HeightfieldNodePointer(_root->readSubdivision(state))); + } +} + QByteArray Heightfield::getRendererClassName() const { return "HeightfieldRenderer"; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 0b24c7e318..c832d3fbd5 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -592,6 +592,8 @@ public: virtual void readExtra(Bitstream& in); virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + virtual void writeExtraSubdivision(Bitstream& out); + virtual void readExtraSubdivision(Bitstream& in); signals: From 3fbbd6c2dee3c5ed8f759c64dedf7f7109d2eef1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Dec 2014 16:43:03 -0800 Subject: [PATCH 11/29] Working on transitioning functions to quadtree equivalents. --- libraries/metavoxels/src/SharedObject.cpp | 4 +- libraries/metavoxels/src/SharedObject.h | 3 +- libraries/metavoxels/src/Spanner.cpp | 441 ++++++++++++---------- libraries/metavoxels/src/Spanner.h | 6 +- 4 files changed, 252 insertions(+), 202 deletions(-) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 8f57c8be34..95dc75fec0 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -151,8 +151,8 @@ void SharedObject::writeExtraSubdivision(Bitstream& out) { // nothing by default } -void SharedObject::readExtraSubdivision(Bitstream& in) { - // nothing by default +SharedObject* SharedObject::readExtraSubdivision(Bitstream& in) { + return this; } QAtomicInt SharedObject::_nextID(1); diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index f35fde802c..cf9bf4e645 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -96,7 +96,8 @@ public: virtual void writeExtraSubdivision(Bitstream& out); /// Reads the subdivision of the non-property contents of this object from the specified stream. - virtual void readExtraSubdivision(Bitstream& in); + /// \return the modified object, or this if no modification was performed + virtual SharedObject* readExtraSubdivision(Bitstream& in); private: diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index bdccb18b7e..f8857a36d3 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1242,6 +1242,235 @@ bool HeightfieldNode::isLeaf() const { return true; } +float HeightfieldNode::getHeight(const glm::vec3& location) const { + 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; + if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { + return -FLT_MAX; + } + + // 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& origin, const glm::vec3& direction, float& distance) const { + if (!isLeaf()) { + float closestDistance = FLT_MAX; + for (int i = 0; i < CHILD_COUNT; i++) { + 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)) { + closestDistance = qMin(closestDistance, childDistance); + } + } + if (closestDistance == FLT_MAX) { + return false; + } + distance = closestDistance; + return true; + } + 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 scale((float)innerWidth, (float)numeric_limits::max(), (float)innerHeight); + glm::vec3 dir = direction * scale; + glm::vec3 entry = origin * scale; + + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), + (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance)) { + return false; + } + entry += 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; +} + void HeightfieldNode::read(HeightfieldStreamState& state) { clearChildren(); @@ -1704,206 +1933,16 @@ bool Heightfield::isHeightfield() const { } float Heightfield::getHeight(const glm::vec3& location) const { - 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 = glm::inverse(getRotation()) * (location - getTranslation()) * glm::vec3(1.0f / getScale(), - 1.0f, 1.0f / (getScale() * _aspectZ)); - relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; - relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { - return -FLT_MAX; - } - - // 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 - } - - // convert the interpolated height into world space - return getTranslation().y + interpolatedHeight * getScale() * _aspectY / numeric_limits::max(); + 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); } bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, 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::quat inverseRotation = glm::inverse(getRotation()); - glm::vec3 inverseScale(innerWidth / getScale(), numeric_limits::max() / (getScale() * _aspectY), - innerHeight / (getScale() * _aspectZ)); - glm::vec3 dir = inverseRotation * direction * inverseScale; - glm::vec3 entry = inverseRotation * (origin - getTranslation()) * inverseScale; - - float boundsDistance; - if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), - (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance)) { - return false; - } - entry += 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; + 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); } Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, @@ -2747,7 +2786,7 @@ void Heightfield::writeExtraSubdivision(Bitstream& out) { } } -void Heightfield::readExtraSubdivision(Bitstream& in) { +SharedObject* Heightfield::readExtraSubdivision(Bitstream& in) { MetavoxelLOD lod, referenceLOD; if (in.getContext()) { MetavoxelStreamBase* base = static_cast(in.getContext()); @@ -2758,8 +2797,14 @@ void Heightfield::readExtraSubdivision(Bitstream& in) { HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; if (state.becameSubdividedOrCollapsed()) { - setRoot(HeightfieldNodePointer(_root->readSubdivision(state))); + HeightfieldNodePointer root(_root->readSubdivision(state)); + if (_root != root) { + Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setRoot(root); + return newHeightfield; + } } + return this; } QByteArray Heightfield::getRendererClassName() const { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index c832d3fbd5..be8602d3a4 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -505,6 +505,10 @@ public: const HeightfieldNodePointer& getChild(int index) const { return _children[index]; } + float getHeight(const glm::vec3& location) const; + + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -593,7 +597,7 @@ public: virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); virtual void writeExtraSubdivision(Bitstream& out); - virtual void readExtraSubdivision(Bitstream& in); + virtual SharedObject* readExtraSubdivision(Bitstream& in); signals: From 46f1fc7c0f6ab1c9c27ae0cbccd64a119fbb1cda Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Dec 2014 18:50:24 -0800 Subject: [PATCH 12/29] More work on spanner heightfield edits. --- libraries/metavoxels/src/Spanner.cpp | 238 +++++++++++++++------------ libraries/metavoxels/src/Spanner.h | 11 +- 2 files changed, 147 insertions(+), 102 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f8857a36d3..9e1248524f 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1471,6 +1471,132 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::ve return false; } +HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const glm::vec3& radius, + const SharedObjectPointer& material, const QColor& color) { + 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 || 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; + } + 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; + HeightfieldNode* newNode = new HeightfieldNode(*this); + + 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 = baseWidth, materialHeight = baseHeight; + QByteArray materialContents; + QVector materials; + if (_material) { + materialWidth = _material->getWidth(); + materialHeight = _material->getContents().size() / materialWidth; + materialContents = _material->getContents(); + materials = _material->getMaterials(); + + } else { + materialContents = QByteArray(baseWidth * baseHeight, 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; + } + if (changed) { + 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) { + clearUnusedMaterials(materials, materialContents); + newNode->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, + materialContents, materials))); + } + + return newNode; +} + void HeightfieldNode::read(HeightfieldStreamState& state) { clearChildren(); @@ -1718,7 +1844,7 @@ void HeightfieldNode::clearChildren() { } } -void HeightfieldNode::mergeChildren() { +void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { if (isLeaf()) { return; } @@ -1751,7 +1877,7 @@ void HeightfieldNode::mergeChildren() { materialHeight = qMax(materialHeight, childMaterialHeight); } } - if (heightWidth > 0) { + if (heightWidth > 0 && height) { QVector heightContents(heightWidth * heightHeight); for (int i = 0; i < CHILD_COUNT; i++) { HeightfieldHeightPointer childHeight = _children[i]->getHeight(); @@ -1780,9 +1906,12 @@ void HeightfieldNode::mergeChildren() { } _height = new HeightfieldHeight(heightWidth, heightContents); - } else { + } else if (height) { _height.reset(); } + if (!colorMaterial) { + return; + } if (colorWidth > 0) { QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0); for (int i = 0; i < CHILD_COUNT; i++) { @@ -1947,107 +2076,14 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) { - if (!_height) { + 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; } - 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; Heightfield* newHeightfield = static_cast(clone(true)); - - 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 = baseWidth, materialHeight = baseHeight; - QByteArray materialContents; - QVector materials; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - materialContents = _material->getContents(); - materials = _material->getMaterials(); - - } else { - materialContents = QByteArray(baseWidth * baseHeight, 0); - } - - int highestX = colorWidth - 1; - int highestZ = colorHeight - 1; - glm::vec3 inverseScale(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); - glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; - - glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // paint all points within the radius - float z = qMax(start.z, 0.0f); - float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX); - 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 = inverseScale.x / inverseScale.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; - } - if (changed) { - newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents))); - } - - highestX = materialWidth - 1; - highestZ = materialHeight - 1; - inverseScale = glm::vec3(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ)); - center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; - - extents = glm::vec3(radius, radius, radius) * inverseScale; - 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; - 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) { - clearUnusedMaterials(materials, materialContents); - newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, - materialContents, materials))); - } - + newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index be8602d3a4..2173bd67e9 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -497,18 +497,27 @@ public: void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, const HeightfieldMaterialPointer& material); + void setHeight(const HeightfieldHeightPointer& height) { _height = height; } const HeightfieldHeightPointer& getHeight() const { return _height; } + + void setColor(const HeightfieldColorPointer& color) { _color = color; } const HeightfieldColorPointer& getColor() const { return _color; } + + void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; } const HeightfieldMaterialPointer& getMaterial() const { return _material; } bool isLeaf() const; + 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& 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 read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -526,7 +535,7 @@ public: private: void clearChildren(); - void mergeChildren(); + void mergeChildren(bool height = true, bool colorMaterial = true); HeightfieldHeightPointer _height; HeightfieldColorPointer _color; From a46b46b5a2b1bfaf453aea440956723a4e481167 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Dec 2014 19:10:10 -0800 Subject: [PATCH 13/29] Fix for merging. --- libraries/metavoxels/src/Spanner.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 9e1248524f..301959b16f 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1913,9 +1913,12 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { return; } if (colorWidth > 0) { - QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 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) { @@ -1953,6 +1956,9 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { 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) { From a022b7fa172f1f64236b72d4f1c84b6cefe9b82c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 13:12:45 -0800 Subject: [PATCH 14/29] Fix for successive heightfield placements. --- interface/src/ui/MetavoxelEditor.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 6 ++++++ libraries/metavoxels/src/Spanner.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6d1e181907..a88b981d66 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -789,7 +789,7 @@ void ImportHeightfieldTool::apply() { if (!(_height->getHeight() && attribute)) { return; } - MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, _spanner)) }; + MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, _spanner->clone())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); } diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 301959b16f..cb8cdc6260 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -2063,6 +2063,12 @@ MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { qMax(0.5f, glm::abs(position.y * _aspectY - 0.5f)) * THRESHOLD_MULTIPLIER); } +SharedObject* Heightfield::clone(bool withID, SharedObject* target) const { + Heightfield* newHeightfield = static_cast(Spanner::clone(withID, target)); + newHeightfield->setRoot(_root); + return newHeightfield; +} + bool Heightfield::isHeightfield() const { return true; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 2173bd67e9..6c38dbcb9a 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -579,6 +579,8 @@ public: MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const; + virtual SharedObject* clone(bool withID = false, SharedObject* target = NULL) const; + virtual bool isHeightfield() const; virtual float getHeight(const glm::vec3& location) const; From 284cd54e28d3d75438cbed70f7e31f18b7e85036 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 14:33:54 -0800 Subject: [PATCH 15/29] More detachment fixes. --- interface/src/ui/MetavoxelEditor.cpp | 25 ++++++------------------- interface/src/ui/MetavoxelEditor.h | 5 ++--- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index a88b981d66..6989c47d67 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -182,13 +182,6 @@ QVariant MetavoxelEditor::getValue() const { return editor ? editor->metaObject()->userProperty().read(editor) : QVariant(); } -void MetavoxelEditor::detachValue() { - SharedObjectEditor* editor = qobject_cast(_valueArea->widget()); - if (editor) { - editor->detachObject(); - } -} - bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { // pass along to the active tool MetavoxelTool* tool = getActiveTool(); @@ -616,7 +609,7 @@ PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, } void PlaceSpannerTool::simulate(float deltaTime) { - Spanner* spanner = static_cast(getSpanner(true).data()); + Spanner* spanner = static_cast(getSpanner().data()); Transformable* transformable = qobject_cast(spanner); if (transformable && _followMouse->isChecked() && !Application::getInstance()->isMouseHidden()) { // find the intersection of the mouse ray with the grid and place the transformable there @@ -649,10 +642,7 @@ bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) { return false; } -SharedObjectPointer PlaceSpannerTool::getSpanner(bool detach) { - if (detach) { - _editor->detachValue(); - } +SharedObjectPointer PlaceSpannerTool::getSpanner() { return _editor->getValue().value(); } @@ -663,7 +653,7 @@ QColor PlaceSpannerTool::getColor() { void PlaceSpannerTool::place() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (attribute) { - applyEdit(attribute, getSpanner()); + applyEdit(attribute, getSpanner()->clone()); } } @@ -897,7 +887,8 @@ MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool cleara SharedObjectPointer MaterialControl::getMaterial() { SharedObjectPointer material = _materialEditor->getObject(); if (static_cast(material.data())->getDiffuse().isValid()) { - _materialEditor->detachObject(); + material = material->clone(); + } else { material = SharedObjectPointer(); } @@ -1014,10 +1005,7 @@ bool VoxelMaterialSpannerTool::appliesTo(const AttributePointer& attribute) cons return attribute->inherits("VoxelColorAttribute"); } -SharedObjectPointer VoxelMaterialSpannerTool::getSpanner(bool detach) { - if (detach) { - _spannerEditor->detachObject(); - } +SharedObjectPointer VoxelMaterialSpannerTool::getSpanner() { return _spannerEditor->getObject(); } @@ -1026,7 +1014,6 @@ QColor VoxelMaterialSpannerTool::getColor() { } void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { - _spannerEditor->detachObject(); MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, _materialControl->getMaterial(), _materialControl->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 4e870a9982..c0433fc3cd 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -46,7 +46,6 @@ public: glm::quat getGridRotation() const; QVariant getValue() const; - void detachValue(); virtual bool eventFilter(QObject* watched, QEvent* event); @@ -197,7 +196,7 @@ public: protected: virtual QColor getColor(); - virtual SharedObjectPointer getSpanner(bool detach = false); + virtual SharedObjectPointer getSpanner(); virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0; protected slots: @@ -422,7 +421,7 @@ public: protected: - virtual SharedObjectPointer getSpanner(bool detach = false); + virtual SharedObjectPointer getSpanner(); virtual QColor getColor(); virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner); From 9753041a1a3c01524dc9f69444a19f8110c7b3af Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 16:11:29 -0800 Subject: [PATCH 16/29] Height brush bits. --- libraries/metavoxels/src/Spanner.cpp | 276 ++++++++++++++++++--------- libraries/metavoxels/src/Spanner.h | 6 + 2 files changed, 187 insertions(+), 95 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index cb8cdc6260..ee38ef86e4 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1243,6 +1243,9 @@ bool HeightfieldNode::isLeaf() const { } 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) { @@ -1271,9 +1274,6 @@ float HeightfieldNode::getHeight(const glm::vec3& location) const { glm::vec3 relative = location; relative.x = relative.x * innerWidth + HeightfieldHeight::HEIGHT_BORDER; relative.z = relative.z * innerHeight + HeightfieldHeight::HEIGHT_BORDER; - if (relative.x < 0.0f || relative.z < 0.0f || relative.x > width - 1 || relative.z > height - 1) { - return -FLT_MAX; - } // find the bounds of the cell containing the point and the shared vertex heights glm::vec3 floors = glm::floor(relative); @@ -1304,6 +1304,10 @@ float HeightfieldNode::getHeight(const glm::vec3& location) const { } bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float boundsDistance; + if (!Box(glm::vec3(), glm::vec3(1.0f, 1.0f, 1.0f)).findRayIntersection(origin, direction, boundsDistance)) { + return false; + } if (!isLeaf()) { float closestDistance = FLT_MAX; for (int i = 0; i < CHILD_COUNT; i++) { @@ -1334,14 +1338,7 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::ve glm::vec3 scale((float)innerWidth, (float)numeric_limits::max(), (float)innerHeight); glm::vec3 dir = direction * scale; - glm::vec3 entry = origin * scale; - - float boundsDistance; - if (!Box(glm::vec3(), glm::vec3((float)innerWidth, (float)numeric_limits::max(), - (float)innerHeight)).findRayIntersection(entry, dir, boundsDistance)) { - return false; - } - entry += dir * boundsDistance; + glm::vec3 entry = origin * scale + dir * boundsDistance; entry.x += HeightfieldHeight::HEIGHT_BORDER; entry.z += HeightfieldHeight::HEIGHT_BORDER; @@ -1473,6 +1470,10 @@ bool HeightfieldNode::findRayIntersection(const glm::vec3& origin, const glm::ve 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++) { @@ -1491,15 +1492,13 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const } return newNode; } - if (!_height || position.x + radius.x < 0.0f || position.z + radius.z < 0.0f || - position.x - radius.x > 1.0f || position.z - radius.z > 1.0f) { + 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; - HeightfieldNode* newNode = new HeightfieldNode(*this); int colorWidth = baseWidth, colorHeight = baseHeight; QByteArray colorContents; @@ -1556,7 +1555,9 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const } lineDest += stride; } + HeightfieldNode* newNode = this; if (changed) { + newNode = new HeightfieldNode(*this); newNode->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents))); } @@ -1589,6 +1590,9 @@ HeightfieldNode* HeightfieldNode::paintMaterial(const glm::vec3& position, const lineDest += materialWidth; } if (changed) { + if (newNode == this) { + newNode = new HeightfieldNode(*this); + } clearUnusedMaterials(materials, materialContents); newNode->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth, materialContents, materials))); @@ -1597,6 +1601,153 @@ 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; + } + 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; + + 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); + + // 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; + 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 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); + } + } + } + lineDest += heightWidth; + } +} + +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) { + 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); + 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 (!_height) { + return this; + } + int heightWidth = _height->getWidth(); + int heightHeight = _height->getContents().size() / heightWidth; + QVector contents = _height->getContents(); + int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; + int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; + int highestX = heightWidth - 1; + int highestZ = heightHeight - 1; + + 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); + + // 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; + } + } + } + + // 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; + 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 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; + } + } + } + lineDest += heightWidth; + } + if (!changed) { + return this; + } + HeightfieldNode* newNode = new HeightfieldNode(*this); + newNode->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); + return newNode; +} + void HeightfieldNode::read(HeightfieldStreamState& state) { clearChildren(); @@ -2088,7 +2239,7 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3& 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); + 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) { @@ -2100,94 +2251,29 @@ Spanner* Heightfield::paintMaterial(const glm::vec3& position, float radius, } Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float height) { - if (!_height) { - return this; - } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - QVector contents = _height->getContents(); - int innerWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - int highestX = heightWidth - 1; - int highestZ = heightHeight - 1; - Heightfield* newHeightfield = static_cast(clone(true)); - - glm::vec3 inverseScale(innerWidth / getScale(), 1.0f, innerHeight / (getScale() * _aspectZ)); - glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale; - center.x += 1.0f; - center.z += 1.0f; - - glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale; - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - // 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; - float squaredRadius = extents.x * extents.x; - float squaredRadiusReciprocal = 1.0f / squaredRadius; - float scaledHeight = height * numeric_limits::max() / (getScale() * _aspectY); - float multiplierZ = inverseScale.x / inverseScale.z; + 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(); - for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { - quint16* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest; - if (value != 0) { - value += scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - minimumValue = qMin(minimumValue, value); - maximumValue = qMax(maximumValue, value); - } - } - } - lineDest += heightWidth; - } - + _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()) { - float scale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); - float offset = 1.0f - minimumValue; - newHeightfield->setAspectY(_aspectY / scale); + normalizeScale = (numeric_limits::max() - 1.0f) / (maximumValue - minimumValue); + normalizeOffset = 1.0f - minimumValue; + newHeightfield->setAspectY(_aspectY / normalizeScale); newHeightfield->setTranslation(getTranslation() - getRotation() * - glm::vec3(0.0f, offset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); - for (quint16* dest = contents.data(), *end = contents.data() + contents.size(); dest != end; dest++) { - int value = *dest; - if (value != 0) { - *dest = (value + offset) * scale; - } - } + glm::vec3(0.0f, normalizeOffset * _aspectY * getScale() / (numeric_limits::max() - 1), 0.0f)); } // now apply the actual change - z = qMax(start.z, 0.0f); - lineDest = contents.data() + (int)z * heightWidth + (int)startX; - scaledHeight = height * numeric_limits::max() / (getScale() * newHeightfield->getAspectY()); - bool changed = false; - for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) { - quint16* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = (z - center.z) * multiplierZ; - float distanceSquared = dx * dx + dz * dz; - if (distanceSquared <= squaredRadius) { - // height falls off towards edges - int value = *dest; - if (value != 0) { - *dest = value + scaledHeight * (squaredRadius - distanceSquared) * squaredRadiusReciprocal; - changed = true; - } - } - } - lineDest += heightWidth; - } - if (changed) { - newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, contents))); - } - + newHeightfield->setRoot(HeightfieldNodePointer(_root->paintHeight(relativePosition, relativeRadius, + height * numeric_limits::max() / (getScale() * newHeightfield->getAspectY()), + normalizeScale, normalizeOffset))); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6c38dbcb9a..f3e7c6bfdb 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -517,6 +517,12 @@ 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; + + HeightfieldNode* paintHeight(const glm::vec3& position, const glm::vec3& radius, float height, + float normalizeScale, float normalizeOffset); void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; From 1aec214138d48a4681a0472ed7692635f926b871 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 2 Dec 2014 16:23:09 -0800 Subject: [PATCH 17/29] gabbing the AnbientCOlor value, we will use it as the lighmap global level --- libraries/fbx/src/FBXReader.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5f215ac4d0..0c52a4b55b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1276,6 +1276,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, #endif FBXGeometry geometry; float unitScaleFactor = 1.0f; + glm::vec3 ambientColor; foreach (const FBXNode& child, node.children) { if (child.name == "FBXHeaderExtension") { @@ -1302,10 +1303,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else if (child.name == "GlobalSettings") { foreach (const FBXNode& object, child.children) { if (object.name == "Properties70") { + QString propertyName = "P"; + int index = 4; foreach (const FBXNode& subobject, object.children) { - if (subobject.name == "P" && subobject.properties.size() >= 5 && - subobject.properties.at(0) == "UnitScaleFactor") { - unitScaleFactor = subobject.properties.at(4).toFloat(); + if (subobject.name == propertyName) { + std::string subpropName = subobject.properties.at(0).toString().toStdString(); + if (subpropName == "UnitScaleFactor") { + unitScaleFactor = subobject.properties.at(index).toFloat(); + } else if (subpropName == "AmbientColor") { + ambientColor = getVec3(subobject.properties, index); + } } } } From 86693980489f5088e768b6087e14dbe88d5da661 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 19:14:33 -0800 Subject: [PATCH 18/29] Voxelization bits. --- libraries/metavoxels/src/Spanner.cpp | 579 +++++++++++++++------------ libraries/metavoxels/src/Spanner.h | 6 + 2 files changed, 336 insertions(+), 249 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index ee38ef86e4..69bc919e9c 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1748,6 +1748,279 @@ HeightfieldNode* HeightfieldNode::paintHeight(const glm::vec3& position, const g return newNode; } +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::read(HeightfieldStreamState& state) { clearChildren(); @@ -2152,6 +2425,59 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) { } } +QRgb HeightfieldNode::getColorAt(const glm::vec3& location) const { + if (location.x < 0.0f || location.z < 0.0f || location.x > 1.0f || location.z > 1.0f) { + return 0; + } + int width = _color->getWidth(); + const QByteArray& contents = _color->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / (width * DataBlock::COLOR_BYTES); + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = location * glm::vec3((float)innerWidth, 1.0f, (float)innerHeight); + 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; + const uchar* upperLeft = src + (floorZ * width + floorX) * DataBlock::COLOR_BYTES; + const uchar* lowerRight = src + (ceilZ * width + ceilX) * DataBlock::COLOR_BYTES; + glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + const uchar* upperRight = src + (floorZ * width + ceilX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + const uchar* lowerLeft = src + (ceilZ * width + floorX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); + } + return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); +} + +int HeightfieldNode::getMaterialAt(const glm::vec3& location) const { + if (location.x < 0.0f || location.z < 0.0f || location.x > 1.0f || location.z > 1.0f) { + return -1; + } + int width = _material->getWidth(); + const QByteArray& contents = _material->getContents(); + const uchar* src = (const uchar*)contents.constData(); + int height = contents.size() / width; + int innerWidth = width - HeightfieldData::SHARED_EDGE; + int innerHeight = height - HeightfieldData::SHARED_EDGE; + + glm::vec3 relative = location * glm::vec3((float)innerWidth, 1.0f, (float)innerHeight); + return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; +} + Heightfield::Heightfield() : _aspectY(1.0f), _aspectZ(1.0f) { @@ -2278,258 +2604,13 @@ Spanner* Heightfield::paintHeight(const glm::vec3& position, float radius, float } Spanner* Heightfield::clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield) { - if (!_height) { + HeightfieldNode* newRoot = _root->clearAndFetchHeight(getTranslation(), getRotation(), + glm::vec3(getScale(), getScale() * _aspectY, getScale() * _aspectZ), bounds, heightfield); + if (_root == newRoot) { return this; } - int heightWidth = _height->getWidth(); - int heightHeight = _height->getContents().size() / heightWidth; - int innerHeightWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION; - int innerHeightHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION; - float heightIncrementX = getScale() / innerHeightWidth; - float heightIncrementZ = (getScale() * _aspectZ) / innerHeightHeight; - - int colorWidth = heightWidth; - int colorHeight = heightHeight; - if (_color) { - colorWidth = _color->getWidth(); - colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES); - } - int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE; - int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE; - float colorIncrementX = getScale() / innerColorWidth; - float colorIncrementZ = (getScale() * _aspectZ) / innerColorHeight; - - int materialWidth = colorWidth; - int materialHeight = colorHeight; - if (_material) { - materialWidth = _material->getWidth(); - materialHeight = _material->getContents().size() / materialWidth; - } - int innerMaterialWidth = materialWidth - HeightfieldData::SHARED_EDGE; - int innerMaterialHeight = materialHeight - HeightfieldData::SHARED_EDGE; - float materialIncrementX = getScale() / innerMaterialWidth; - float materialIncrementZ = (getScale() * _aspectZ) / innerMaterialHeight; - - float largestIncrementX = qMax(heightIncrementX, qMax(colorIncrementX, materialIncrementX)); - float largestIncrementZ = qMax(heightIncrementZ, qMax(colorIncrementZ, materialIncrementZ)); - - glm::vec3 minimum(glm::floor(bounds.minimum.x / largestIncrementX) * largestIncrementX, getBounds().minimum.y, - glm::floor(bounds.minimum.z / largestIncrementZ) * largestIncrementZ); - glm::vec3 maximum(glm::ceil(bounds.maximum.x / largestIncrementX) * largestIncrementX, getBounds().maximum.y, - glm::ceil(bounds.maximum.z / largestIncrementZ) * largestIncrementZ); - Box largestBounds(minimum, maximum); - - // 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(getRotation())) * glm::translate(-getTranslation()); - glm::vec3 inverseScale(innerHeightWidth / getScale(), 1.0f, innerHeightHeight / (getScale() * _aspectZ)); - glm::mat4 transform = glm::scale(inverseScale) * baseTransform; - Box transformedBounds = transform * largestBounds; - - // make sure there are values to clear - int startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x) + HeightfieldHeight::HEIGHT_BORDER, - 0, heightWidth - 1); - int startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z) + HeightfieldHeight::HEIGHT_BORDER, - 0, heightHeight - 1); - int endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x) + HeightfieldHeight::HEIGHT_BORDER, 0, heightWidth - 1); - int endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z) + HeightfieldHeight::HEIGHT_BORDER, - 0, heightHeight - 1); - const quint16* src = _height->getContents().constData() + startZ * heightWidth + startX; - for (int z = startZ; z <= endZ; z++, src += heightWidth) { - const quint16* lineSrc = src; - for (int x = startX; x <= endX; x++) { - if (*lineSrc++ != 0) { - goto clearableBreak; - } - } - } - return this; - clearableBreak: - - 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 * getBounds(); - 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::vec3(spannerStartX - HeightfieldHeight::HEIGHT_BORDER, 0, - spannerStartZ - HeightfieldHeight::HEIGHT_BORDER) * step + spanner->getTranslation(); - glm::vec3 position = initialPosition; - float heightScale = numeric_limits::max() / (getScale() * _aspectY); - 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) - getTranslation().y) * 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 * getBounds(); - 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::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); - position = initialPosition; - 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 * getBounds(); - 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::vec3(spannerStartX, 0, spannerStartZ) * step + spanner->getTranslation(); - position = initialPosition; - 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)); - } - - // if we've cleared all the inner height, we can remove the spanner entirely - src = newHeightContents.constData() + heightWidth + HeightfieldHeight::HEIGHT_BORDER; - for (int z = 0; z < innerHeightHeight; z++, src += heightWidth) { - const quint16* lineSrc = src; - for (int x = 0; x < innerHeightWidth; x++) { - if (*lineSrc++ != 0) { - goto nonEmptyBreak; - } - } - } - return NULL; - nonEmptyBreak: - Heightfield* newHeightfield = static_cast(clone(true)); - newHeightfield->setHeight(HeightfieldHeightPointer(new HeightfieldHeight(heightWidth, newHeightContents))); - - // and the color - if (_color) { - inverseScale = glm::vec3(innerColorWidth / getScale(), 1.0f, innerColorHeight / (getScale() * _aspectZ)); - transform = glm::scale(inverseScale) * baseTransform; - transformedBounds = transform * largestBounds; - startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, colorWidth - 1); - startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, colorHeight - 1); - endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, colorWidth - 1); - endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, colorHeight - 1); - QByteArray newColorContents = _color->getContents(); - char* dest = newColorContents.data() + (startZ * colorWidth + startX) * DataBlock::COLOR_BYTES; - for (int z = startZ; z <= endZ; z++, dest += colorWidth * DataBlock::COLOR_BYTES) { - memset(dest, 0, (endX - startX + 1) * DataBlock::COLOR_BYTES); - } - newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, newColorContents))); - } - - // and the material - if (_material) { - inverseScale = glm::vec3(innerMaterialWidth / getScale(), 1.0f, innerMaterialHeight / (getScale() * _aspectZ)); - transform = glm::scale(inverseScale) * baseTransform; - transformedBounds = transform * largestBounds; - startX = glm::clamp((int)glm::ceil(transformedBounds.minimum.x), 0, materialWidth - 1); - startZ = glm::clamp((int)glm::ceil(transformedBounds.minimum.z), 0, materialHeight - 1); - endX = glm::clamp((int)glm::floor(transformedBounds.maximum.x), 0, materialWidth - 1); - endZ = glm::clamp((int)glm::floor(transformedBounds.maximum.z), 0, materialHeight - 1); - QByteArray newMaterialContents = _material->getContents(); - QVector newMaterials = _material->getMaterials(); - char* dest = newMaterialContents.data() + startZ * materialWidth + startX; - for (int z = startZ; z <= endZ; z++, dest += materialWidth) { - memset(dest, 0, endX - startX + 1); - } - clearUnusedMaterials(newMaterials, newMaterialContents); - newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial( - materialWidth, newMaterialContents, newMaterials))); - } - + newHeightfield->setRoot(HeightfieldNodePointer(newRoot)); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index f3e7c6bfdb..6742324f85 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -524,6 +524,9 @@ public: 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); + void read(HeightfieldStreamState& state); void write(HeightfieldStreamState& state) const; @@ -543,6 +546,9 @@ private: void clearChildren(); void mergeChildren(bool height = true, bool colorMaterial = true); + QRgb getColorAt(const glm::vec3& location) const; + int getMaterialAt(const glm::vec3& location) const; + HeightfieldHeightPointer _height; HeightfieldColorPointer _color; HeightfieldMaterialPointer _material; From 1a1b1ca3f3f2cea68117a9aeb89fe6af4d26aadd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 19:49:27 -0800 Subject: [PATCH 19/29] Fix for voxelizing heightfields. --- interface/src/ui/MetavoxelEditor.cpp | 1 + libraries/metavoxels/src/Spanner.cpp | 14 +++++++++++++- libraries/metavoxels/src/Spanner.h | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 6989c47d67..26b403e423 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1014,6 +1014,7 @@ QColor VoxelMaterialSpannerTool::getColor() { } void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) { + static_cast(spanner.data())->setWillBeVoxelized(true); MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner, _materialControl->getMaterial(), _materialControl->getColor())) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 69bc919e9c..f249ae2912 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -61,7 +61,8 @@ Spanner::Spanner() : _renderer(NULL), _placementGranularity(DEFAULT_PLACEMENT_GRANULARITY), _voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY), - _merged(false) { + _merged(false), + _willBeVoxelized(false) { } void Spanner::setBounds(const Box& bounds) { @@ -2542,6 +2543,9 @@ MetavoxelLOD Heightfield::transformLOD(const MetavoxelLOD& lod) const { SharedObject* Heightfield::clone(bool withID, SharedObject* target) const { Heightfield* newHeightfield = static_cast(Spanner::clone(withID, target)); + newHeightfield->setHeight(_height); + newHeightfield->setColor(_color); + newHeightfield->setMaterial(_material); newHeightfield->setRoot(_root); return newHeightfield; } @@ -2920,6 +2924,10 @@ bool Heightfield::intersects(const glm::vec3& start, const glm::vec3& end, float } void Heightfield::writeExtra(Bitstream& out) const { + if (getWillBeVoxelized()) { + out << _height << _color << _material; + return; + } MetavoxelLOD lod; if (out.getContext()) { lod = transformLOD(static_cast(out.getContext())->lod); @@ -2930,6 +2938,10 @@ void Heightfield::writeExtra(Bitstream& out) const { } void Heightfield::readExtra(Bitstream& in) { + if (getWillBeVoxelized()) { + in >> _height >> _color >> _material; + return; + } MetavoxelLOD lod; if (in.getContext()) { lod = transformLOD(static_cast(in.getContext())->lod); diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 6742324f85..c4fe185a58 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -29,6 +29,7 @@ class Spanner : public SharedObject { Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false) Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false) Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false) + Q_PROPERTY(bool willBeVoxelized MEMBER _willBeVoxelized DESIGNABLE false) public: @@ -49,6 +50,9 @@ public: void setMerged(bool merged) { _merged = merged; } bool isMerged() const { return _merged; } + void setWillBeVoxelized(bool willBeVoxelized) { _willBeVoxelized = willBeVoxelized; } + bool getWillBeVoxelized() const { return _willBeVoxelized; } + /// Checks whether we've visited this object on the current traversal. If we have, returns false. /// If we haven't, sets the last visit identifier and returns true. bool testAndSetVisited(int visit); @@ -118,6 +122,7 @@ private: float _placementGranularity; float _voxelizationGranularity; bool _merged; + bool _willBeVoxelized; QHash _lastVisits; ///< last visit identifiers for each thread QMutex _lastVisitsMutex; From 94d0612804ead5d3c41f42d4afd4870a6b80e3d6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 20:17:18 -0800 Subject: [PATCH 20/29] Fix for Hermite display. --- interface/src/MetavoxelSystem.cpp | 55 ++++++++++++++++++++----------- interface/src/MetavoxelSystem.h | 11 +++++++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a4d8c6ff37..6a227614cb 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -461,6 +461,34 @@ void MetavoxelSystem::render() { _voxelBaseBatches.clear(); } + if (!_hermiteBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); + + glEnableClientState(GL_VERTEX_ARRAY); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glNormal3f(0.0f, 1.0f, 0.0f); + + Application::getInstance()->getDeferredLightingEffect()->bindSimpleProgram(); + + foreach (const HermiteBatch& batch, _hermiteBatches) { + batch.vertexBuffer->bind(); + + glVertexPointer(3, GL_FLOAT, 0, 0); + + glDrawArrays(GL_LINES, 0, batch.vertexCount); + + batch.vertexBuffer->release(); + } + + Application::getInstance()->getDeferredLightingEffect()->releaseSimpleProgram(); + + glDisableClientState(GL_VERTEX_ARRAY); + + Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); + } + _hermiteBatches.clear(); + // give external parties a chance to join in emit rendering(); } @@ -1229,31 +1257,18 @@ void VoxelBuffer::render(bool cursor) { } } - if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) { + if (_hermiteCount > 0) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); - _hermiteBuffer.allocate(_hermite.constData(), _hermite.size() * sizeof(glm::vec3)); + _hermiteBuffer.allocate(_hermite.constData(), _hermite.size() * sizeof(glm::vec3)); + _hermiteBuffer.release(); _hermite.clear(); - - } else { - _hermiteBuffer.bind(); } - - glVertexPointer(3, GL_FLOAT, 0, 0); - - Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().bind(); - - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glNormal3f(0.0f, 1.0f, 0.0f); - - glLineWidth(1.0f); - - glDrawArrays(GL_LINES, 0, _hermiteCount); - - Application::getInstance()->getDeferredLightingEffect()->getSimpleProgram().release(); - - _hermiteBuffer.release(); + HermiteBatch hermiteBatch; + hermiteBatch.vertexBuffer = &_hermiteBuffer; + hermiteBatch.vertexCount = _hermiteCount; + Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 059ef27de4..99c55549c7 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -26,6 +26,7 @@ class HeightfieldBaseLayerBatch; class HeightfieldRendererNode; class HeightfieldSplatBatch; +class HermiteBatch; class Model; class VoxelBatch; class VoxelSplatBatch; @@ -89,6 +90,8 @@ public: void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); } void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } + void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); } + signals: void rendering(); @@ -121,6 +124,7 @@ private: QVector _heightfieldSplatBatches; QVector _voxelBaseBatches; QVector _voxelSplatBatches; + QVector _hermiteBatches; ProgramObject _baseHeightfieldProgram; int _baseHeightScaleLocation; @@ -212,6 +216,13 @@ public: int materialIndex; }; +/// A batch containing Hermite data for debugging. +class HermiteBatch { +public: + QOpenGLBuffer* vertexBuffer; + int vertexCount; +}; + /// Generic abstract base class for objects that handle a signal. class SignalHandler : public QObject { Q_OBJECT From 913007939ba6bd821f0d2f11d22fd78695b0419d Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 3 Dec 2014 14:12:50 -0800 Subject: [PATCH 21/29] Trying to read the light attribute --- interface/src/renderer/GeometryCache.cpp | 53 +++++++++++++------ libraries/fbx/src/FBXReader.cpp | 66 +++++++++++++++++++++++- 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index e523d7e608..b64b8f1a90 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -829,26 +829,45 @@ void GeometryReader::run() { return; } try { - std::string urlname = _url.path().toLower().toStdString(); - FBXGeometry fbxgeo; - if (_url.path().toLower().endsWith(".svo")) { - fbxgeo = readSVO(_reply->readAll()); - } else { - bool grabLightmaps = true; - float lightmapLevel = 1.0f; - // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... - if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { - grabLightmaps = false; - } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { - lightmapLevel = 4.0f; - } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { - lightmapLevel = 3.5f; - } - fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps, lightmapLevel); + if (!_reply) { + throw QString("Reply is NULL ?!"); } - QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); + QString urlnameQ = _url.path().toLower(); + std::string urlname = _url.path().toLower().toStdString(); + bool urlValid = true; + urlValid &= !urlname.empty(); + urlValid &= !_url.path().isEmpty(); + urlValid &= _url.path().toLower().endsWith(".fbx") + || _url.path().toLower().endsWith(".svo"); + if (urlValid) { + QString urlnameQ = _url.path().toLower(); + std::string urlnameQstd = urlnameQ.toStdString(); + QByteArray fileBinary = _reply->readAll(); + if (fileBinary.isEmpty() || fileBinary.isNull()) { + throw QString("Read File binary is empty?!"); + } + FBXGeometry fbxgeo; + if (_url.path().toLower().endsWith(".svo")) { + fbxgeo = readSVO(fileBinary); + } else if (_url.path().toLower().endsWith(".fbx")) { + bool grabLightmaps = true; + float lightmapLevel = 1.0f; + // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... + if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { + grabLightmaps = false; + } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { + lightmapLevel = 4.0f; + } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { + lightmapLevel = 3.5f; + } + fbxgeo = readFBX(fileBinary, _mapping, grabLightmaps, lightmapLevel); + } + QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); + } else { + throw QString("url is invalid"); + } // _url.path().toLower().endsWith(".svo") ? readSVO(_reply->readAll()) : readFBX(_reply->readAll(), _mapping))); } catch (const QString& error) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 0c52a4b55b..8eef6ca738 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -32,7 +32,7 @@ // TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems... -//#define DEBUG_FBXREADER +#define DEBUG_FBXREADER using namespace std; @@ -1194,6 +1194,8 @@ int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const Q } } + + FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QHash meshes; QHash modelIDsToNames; @@ -1632,9 +1634,39 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, materials.insert(material.id, material); } else if (object.name == "NodeAttribute") { + std::string attributetype; + const FBXNode* prop70Node = 0; foreach (const FBXNode& subobject, object.children) { + if (subobject.name == "TypeFlags") { typeFlags.insert(getID(object.properties), subobject.properties.at(0).toString()); + attributetype = subobject.properties.at(0).toString().toStdString(); + } else if (subobject.name == "Properties70") { + prop70Node = &subobject; + } + } + + if (!attributetype.empty()) { + if (attributetype == "Light") { + if (prop70Node) { + foreach (const FBXNode& property, prop70Node->children) { + int valIndex = 4; + if (property.name == "P") { + std::string propname = property.properties.at(0).toString().toStdString(); + if (propname == "LightType") { + std::string type = property.properties.at(valIndex).toString().toStdString(); + } else if (propname == "Intensity") { + float intensity = property.properties.at(valIndex).value(); + } +#if defined(DEBUG_FBXREADER) + else { + if (propname == "EmissiveFactor") { + } + } +#endif + } + } + } } } } else if (object.name == "Deformer") { @@ -1674,7 +1706,20 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } } animationCurves.insert(getID(object.properties), curve); + } +#if defined(DEBUG_FBXREADER) + else { + std::string objectname = object.name.data(); + if ( objectname == "Pose" + || objectname == "AnimationStack" + || objectname == "AnimationLayer" + || objectname == "AnimationCurveNode") { + } else { + unknown++; + } + } +#endif } } else if (child.name == "Connections") { foreach (const FBXNode& connection, child.children) { @@ -1726,6 +1771,25 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } } } +#if defined(DEBUG_FBXREADER) + else { + std::string objectname = child.name.data(); + if ( objectname == "Pose" + || objectname == "CreationTime" + || objectname == "FileId" + || objectname == "Creator" + || objectname == "Documents" + || objectname == "References" + || objectname == "Definitions" + || objectname == "Takes" + || objectname == "AnimationStack" + || objectname == "AnimationLayer" + || objectname == "AnimationCurveNode") { + } else { + unknown++; + } + } +#endif } // assign the blendshapes to their corresponding meshes From 8fb766a5d07b6e172c691641893b8c6ac315b201 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 16:44:13 -0800 Subject: [PATCH 22/29] Only recreate the renderers for nodes that have changed. --- interface/src/MetavoxelSystem.cpp | 116 ++++++++++++--------------- interface/src/MetavoxelSystem.h | 36 +++------ libraries/metavoxels/src/Spanner.cpp | 21 ++++- libraries/metavoxels/src/Spanner.h | 18 +++++ 4 files changed, 97 insertions(+), 94 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 6a227614cb..fff91d9052 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -578,6 +578,12 @@ void MetavoxelSystem::setVoxelMaterial(const SharedObjectPointer& spanner, const applyMaterialEdit(edit, true); } +void MetavoxelSystem::deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const { + glDeleteTextures(1, (const GLuint*)&heightTextureID); + glDeleteTextures(1, (const GLuint*)&colorTextureID); + glDeleteTextures(1, (const GLuint*)&materialTextureID); +} + class SpannerRenderVisitor : public SpannerVisitor { public: @@ -2090,48 +2096,10 @@ void StaticModelRenderer::applyURL(const QUrl& url) { HeightfieldRenderer::HeightfieldRenderer() { } -void HeightfieldRenderer::init(Spanner* spanner) { - SpannerRenderer::init(spanner); - - Heightfield* heightfield = static_cast(spanner); - connect(heightfield, &Heightfield::rootChanged, this, &HeightfieldRenderer::updateRoot); - updateRoot(); -} - -void HeightfieldRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { - Heightfield* heightfield = static_cast(_spanner); - _root->render(heightfield, heightfield->transformLOD(lod), glm::vec2(), 1.0f, contained, cursor); -} - -void HeightfieldRenderer::updateRoot() { - Heightfield* heightfield = static_cast(_spanner); - _root = new HeightfieldRendererNode(heightfield->getRoot()); -} - -HeightfieldRendererNode::HeightfieldRendererNode(const HeightfieldNodePointer& heightfieldNode) : - _heightfieldNode(heightfieldNode), - _heightTextureID(0), - _colorTextureID(0), - _materialTextureID(0) { - - for (int i = 0; i < CHILD_COUNT; i++) { - HeightfieldNodePointer child = heightfieldNode->getChild(i); - if (child) { - _children[i] = new HeightfieldRendererNode(child); - } - } -} - -HeightfieldRendererNode::~HeightfieldRendererNode() { - glDeleteTextures(1, &_heightTextureID); - glDeleteTextures(1, &_colorTextureID); - glDeleteTextures(1, &_materialTextureID); -} - const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; -void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLOD& lod, +static void renderNode(const HeightfieldNodePointer& node, Heightfield* heightfield, const MetavoxelLOD& lod, const glm::vec2& minimum, float size, bool contained, bool cursor) { const glm::quat& rotation = heightfield->getRotation(); glm::vec3 scale(heightfield->getScale() * size, heightfield->getScale() * heightfield->getAspectY(), @@ -2148,19 +2116,44 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO contained = true; } } - if (!isLeaf() && lod.shouldSubdivide(minimum, size)) { + if (!node->isLeaf() && lod.shouldSubdivide(minimum, size)) { float nextSize = size * 0.5f; - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->render(heightfield, lod, minimum + glm::vec2(i & X_MAXIMUM_FLAG ? nextSize : 0.0f, + for (int i = 0; i < HeightfieldNode::CHILD_COUNT; i++) { + renderNode(node->getChild(i), heightfield, lod, minimum + glm::vec2(i & X_MAXIMUM_FLAG ? nextSize : 0.0f, i & Y_MAXIMUM_FLAG ? nextSize : 0.0f), nextSize, contained, cursor); } return; } - if (!_heightfieldNode->getHeight()) { + HeightfieldNodeRenderer* renderer = static_cast(node->getRenderer()); + if (!renderer) { + node->setRenderer(renderer = new HeightfieldNodeRenderer()); + } + renderer->render(node, translation, rotation, scale, cursor); +} + +void HeightfieldRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { + Heightfield* heightfield = static_cast(_spanner); + renderNode(heightfield->getRoot(), heightfield, heightfield->transformLOD(lod), glm::vec2(), 1.0f, contained, cursor); +} + +HeightfieldNodeRenderer::HeightfieldNodeRenderer() : + _heightTextureID(0), + _colorTextureID(0), + _materialTextureID(0) { +} + +HeightfieldNodeRenderer::~HeightfieldNodeRenderer() { + QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures", Q_ARG(int, _heightTextureID), + Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); +} + +void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, + const glm::quat& rotation, const glm::vec3& scale, bool cursor) { + if (!node->getHeight()) { return; } - int width = _heightfieldNode->getHeight()->getWidth(); - int height = _heightfieldNode->getHeight()->getContents().size() / width; + int width = node->getHeight()->getWidth(); + int height = node->getHeight()->getContents().size() / width; int innerWidth = width - 2 * HeightfieldHeight::HEIGHT_BORDER; int innerHeight = height - 2 * HeightfieldHeight::HEIGHT_BORDER; int vertexCount = width * height; @@ -2225,7 +2218,7 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO 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 = _heightfieldNode->getHeight()->getContents(); + const QVector& heightContents = node->getHeight()->getContents(); glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, heightContents.constData()); @@ -2234,10 +2227,10 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (_heightfieldNode->getColor()) { - const QByteArray& contents = _heightfieldNode->getColor()->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, _heightfieldNode->getColor()->getWidth(), - contents.size() / (_heightfieldNode->getColor()->getWidth() * DataBlock::COLOR_BYTES), + if (node->getColor()) { + const QByteArray& contents = node->getColor()->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(), + contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES), 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); } else { @@ -2251,13 +2244,13 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO 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); - if (_heightfieldNode->getMaterial()) { - const QByteArray& contents = _heightfieldNode->getMaterial()->getContents(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, _heightfieldNode->getMaterial()->getWidth(), - contents.size() / _heightfieldNode->getMaterial()->getWidth(), + if (node->getMaterial()) { + const QByteArray& contents = node->getMaterial()->getContents(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, node->getMaterial()->getWidth(), + contents.size() / node->getMaterial()->getWidth(), 0, GL_RED, GL_UNSIGNED_BYTE, contents.constData()); - const QVector& materials = _heightfieldNode->getMaterial()->getMaterials(); + const QVector& materials = node->getMaterial()->getMaterials(); _networkTextures.resize(materials.size()); for (int i = 0; i < materials.size(); i++) { const SharedObjectPointer& material = materials.at(i); @@ -2330,7 +2323,7 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); - const QVector& materials = _heightfieldNode->getMaterial()->getMaterials(); + const QVector& materials = node->getMaterial()->getMaterials(); for (int i = 0; i < materials.size(); i += SPLAT_COUNT) { for (int j = 0; j < SPLAT_COUNT; j++) { int index = i + j; @@ -2355,14 +2348,5 @@ void HeightfieldRendererNode::render(Heightfield* heightfield, const MetavoxelLO } } -bool HeightfieldRendererNode::isLeaf() const { - for (int i = 0; i < CHILD_COUNT; i++) { - if (_children[i]) { - return false; - } - } - return true; -} - -QHash HeightfieldRendererNode::_bufferPairs; +QHash HeightfieldNodeRenderer::_bufferPairs; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 99c55549c7..26f3bd68a3 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -24,7 +24,6 @@ #include "renderer/ProgramObject.h" class HeightfieldBaseLayerBatch; -class HeightfieldRendererNode; class HeightfieldSplatBatch; class HermiteBatch; class Model; @@ -91,7 +90,9 @@ public: void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); } void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); } - + + Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const; + signals: void rendering(); @@ -431,8 +432,6 @@ private: Model* _model; }; -typedef QExplicitlySharedDataPointer HeightfieldRendererNodePointer; - /// Renders heightfields. class HeightfieldRenderer : public SpannerRenderer { Q_OBJECT @@ -441,38 +440,21 @@ public: Q_INVOKABLE HeightfieldRenderer(); - virtual void init(Spanner* spanner); virtual void render(const MetavoxelLOD& lod = MetavoxelLOD(), bool contained = false, bool cursor = false); - -private slots: - - void updateRoot(); - -private: - - HeightfieldRendererNodePointer _root; }; -/// A node in the heightfield renderer quadtree. -class HeightfieldRendererNode : public QSharedData { +/// Renders a single quadtree node. +class HeightfieldNodeRenderer : public AbstractHeightfieldNodeRenderer { public: - static const int CHILD_COUNT = 4; + HeightfieldNodeRenderer(); + virtual ~HeightfieldNodeRenderer(); - HeightfieldRendererNode(const HeightfieldNodePointer& heightfieldNode); - virtual ~HeightfieldRendererNode(); - - void render(Heightfield* heightfield, const MetavoxelLOD& lod, const glm::vec2& minimum, float size, - bool contained, bool cursor = false); + void render(const HeightfieldNodePointer& node, const glm::vec3& translation, + const glm::quat& rotation, const glm::vec3& scale, bool cursor); private: - bool isLeaf() const; - - HeightfieldNodePointer _heightfieldNode; - - HeightfieldRendererNodePointer _children[CHILD_COUNT]; - GLuint _heightTextureID; GLuint _colorTextureID; GLuint _materialTextureID; diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f249ae2912..f2a1ebb27f 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1143,7 +1143,23 @@ HeightfieldNode::HeightfieldNode(const HeightfieldHeightPointer& height, const H const HeightfieldMaterialPointer& material) : _height(height), _color(color), - _material(material) { + _material(material), + _renderer(NULL) { +} + +HeightfieldNode::HeightfieldNode(const HeightfieldNode& other) : + _height(other.getHeight()), + _color(other.getColor()), + _material(other.getMaterial()), + _renderer(NULL) { + + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i] = other.getChild(i); + } +} + +HeightfieldNode::~HeightfieldNode() { + delete _renderer; } const int HEIGHT_LEAF_SIZE = 256 + HeightfieldHeight::HEIGHT_EXTENSION; @@ -2479,6 +2495,9 @@ int HeightfieldNode::getMaterialAt(const glm::vec3& location) const { return src[(int)glm::round(relative.z) * width + (int)glm::round(relative.x)]; } +AbstractHeightfieldNodeRenderer::~AbstractHeightfieldNodeRenderer() { +} + Heightfield::Heightfield() : _aspectY(1.0f), _aspectZ(1.0f) { diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index c4fe185a58..bbfcbff1ff 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -17,6 +17,8 @@ #include "AttributeRegistry.h" #include "MetavoxelUtil.h" +class AbstractHeightfieldNodeRenderer; +class Heightfield; class HeightfieldColor; class HeightfieldHeight; class HeightfieldMaterial; @@ -499,6 +501,10 @@ public: const HeightfieldColorPointer& color = HeightfieldColorPointer(), const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer()); + HeightfieldNode(const HeightfieldNode& other); + + ~HeightfieldNode(); + void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color, const HeightfieldMaterialPointer& material); @@ -511,6 +517,9 @@ public: void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; } const HeightfieldMaterialPointer& getMaterial() const { return _material; } + void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; } + AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; } + bool isLeaf() const; void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; } @@ -559,6 +568,15 @@ private: HeightfieldMaterialPointer _material; HeightfieldNodePointer _children[CHILD_COUNT]; + + AbstractHeightfieldNodeRenderer* _renderer; +}; + +/// Base class for heightfield node rendering. +class AbstractHeightfieldNodeRenderer { +public: + + virtual ~AbstractHeightfieldNodeRenderer(); }; /// A heightfield represented as a spanner. From 30d87ee026a3e7e1311160a47515f8ccedeca683 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 4 Dec 2014 00:24:08 -0800 Subject: [PATCH 23/29] Trying to capture light information in order to get the one GLobal Light object containg the intensity value --- libraries/fbx/src/FBXReader.cpp | 114 +++++++++++++++++++++++++------- libraries/fbx/src/FBXReader.h | 9 +++ 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 8eef6ca738..80d8660b9f 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -32,7 +32,7 @@ // TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems... -#define DEBUG_FBXREADER +//#define DEBUG_FBXREADER using namespace std; @@ -1046,6 +1046,7 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } + void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { const glm::vec3& normal = mesh.normals.at(firstIndex); glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); @@ -1195,6 +1196,42 @@ int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const Q } +FBXLight extractLight(const FBXNode& object) { + FBXLight light; + + foreach (const FBXNode& subobject, object.children) { + std::string childname = subobject.name; + if (subobject.name == "Properties70") { + foreach (const FBXNode& property, subobject.children) { + int valIndex = 4; + std::string propName = property.name; + if (property.name == "P") { + std::string propname = property.properties.at(0).toString().toStdString(); + if (propname == "Intensity") { + light.intensity = 0.01f * property.properties.at(valIndex).value(); + } + } + } + } else if ( subobject.name == "GeometryVersion" + || subobject.name == "TypeFlags") { + } + } +#if defined(DEBUG_FBXREADER) + + std::string type = object.properties.at(0).toString().toStdString(); + type = object.properties.at(1).toString().toStdString(); + type = object.properties.at(2).toString().toStdString(); + + foreach (const QVariant& prop, object.properties) { + std::string proptype = prop.typeName(); + std::string propval = prop.toString().toStdString(); + if (proptype == "Properties70") { + } + } +#endif + + return light; +} FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QHash meshes; @@ -1224,6 +1261,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, QHash yComponents; QHash zComponents; + std::map lights; + QVariantHash joints = mapping.value("joint").toHash(); QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); QString jointEyeRightName = processID(getString(joints.value("jointEyeRight", "jointEyeRight"))); @@ -1279,6 +1318,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, FBXGeometry geometry; float unitScaleFactor = 1.0f; glm::vec3 ambientColor; + QString hifiGlobalNodeID; foreach (const FBXNode& child, node.children) { if (child.name == "FBXHeaderExtension") { @@ -1333,6 +1373,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, QString id = getID(object.properties); modelIDsToNames.insert(id, name); + std::string modelname = name.toLower().toStdString(); + if (modelname.find("hifi") == 0) { + hifiGlobalNodeID = id; + } + if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { jointEyeLeftID = getID(object.properties); @@ -1363,6 +1408,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End") { jointRightToeID = getID(object.properties); } + int humanIKJointIndex = humanIKJointNames.indexOf(name); if (humanIKJointIndex != -1) { humanIKJointIDs[humanIKJointIndex] = getID(object.properties); @@ -1459,6 +1505,25 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, extractBlendshape(subobject) }; blendshapes.append(blendshape); } +#if defined(DEBUG_FBXREADER) + else if (subobject.name == "TypeFlags") { + std::string attributetype = subobject.properties.at(0).toString().toStdString(); + if (!attributetype.empty()) { + if (attributetype == "Light") { + std::string lightprop; + foreach (const QVariant& vprop, subobject.properties) { + lightprop = vprop.toString().toStdString(); + } + + FBXLight light = extractLight(object); + } + } + } else { + std::string whatisthat = subobject.name; + if (whatisthat == "WTF") { + } + } +#endif } // add the blendshapes included in the model, if any @@ -1634,41 +1699,28 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, materials.insert(material.id, material); } else if (object.name == "NodeAttribute") { +#if defined(DEBUG_FBXREADER) + std::vector properties; + foreach(const QVariant& v, object.properties) { + properties.push_back(v.toString().toStdString()); + } +#endif + std::string attribID = getID(object.properties).toStdString(); std::string attributetype; - const FBXNode* prop70Node = 0; foreach (const FBXNode& subobject, object.children) { - if (subobject.name == "TypeFlags") { typeFlags.insert(getID(object.properties), subobject.properties.at(0).toString()); attributetype = subobject.properties.at(0).toString().toStdString(); - } else if (subobject.name == "Properties70") { - prop70Node = &subobject; } } if (!attributetype.empty()) { if (attributetype == "Light") { - if (prop70Node) { - foreach (const FBXNode& property, prop70Node->children) { - int valIndex = 4; - if (property.name == "P") { - std::string propname = property.properties.at(0).toString().toStdString(); - if (propname == "LightType") { - std::string type = property.properties.at(valIndex).toString().toStdString(); - } else if (propname == "Intensity") { - float intensity = property.properties.at(valIndex).value(); - } -#if defined(DEBUG_FBXREADER) - else { - if (propname == "EmissiveFactor") { - } - } -#endif - } - } - } + FBXLight light = extractLight(object); + lights[attribID] = light; } } + } else if (object.name == "Deformer") { if (object.properties.last() == "Cluster") { Cluster cluster; @@ -1728,6 +1780,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, QString childID = getID(connection.properties, 1); QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); + if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) { + auto lit = lights.find(childID.toStdString()); + if (lit != lights.end()) { + lightmapLevel = (*lit).second.intensity; + } + } } if (connection.properties.at(0) == "OP") { int counter = 0; @@ -1792,6 +1850,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, #endif } + // TODO: check if is code is needed + if (!lights.empty()) { + if (hifiGlobalNodeID.isEmpty()) { + auto l = (*lights.begin()); + lightmapLevel = (l.second).intensity; + } + } + // assign the blendshapes to their corresponding meshes foreach (const ExtractedBlendshape& extracted, blendshapes) { QString blendshapeChannelID = parentMap.value(extracted.id); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a5df7ccc0c..aacfbc0a98 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -165,6 +165,15 @@ public: QVector rotations; }; +/// A light in an FBX document. +class FBXLight { +public: + QString name; + Transform transform; + float intensity; + glm::vec3 color; +}; + Q_DECLARE_METATYPE(FBXAnimationFrame) Q_DECLARE_METATYPE(QVector) From f4e1af6abc2f0d17f5965a57cbb006765cd56131 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 4 Dec 2014 12:02:30 -0800 Subject: [PATCH 24/29] clean code in GeometryCache around the FBX file loader --- interface/src/renderer/GeometryCache.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index b64b8f1a90..2d876a287f 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -832,7 +832,6 @@ void GeometryReader::run() { if (!_reply) { throw QString("Reply is NULL ?!"); } - QString urlnameQ = _url.path().toLower(); std::string urlname = _url.path().toLower().toStdString(); bool urlValid = true; urlValid &= !urlname.empty(); @@ -841,8 +840,7 @@ void GeometryReader::run() { || _url.path().toLower().endsWith(".svo"); if (urlValid) { - QString urlnameQ = _url.path().toLower(); - std::string urlnameQstd = urlnameQ.toStdString(); + // Let's read the binaries from the network QByteArray fileBinary = _reply->readAll(); if (fileBinary.isEmpty() || fileBinary.isNull()) { throw QString("Read File binary is empty?!"); @@ -868,8 +866,7 @@ void GeometryReader::run() { } else { throw QString("url is invalid"); } - // _url.path().toLower().endsWith(".svo") ? readSVO(_reply->readAll()) : readFBX(_reply->readAll(), _mapping))); - + } catch (const QString& error) { qDebug() << "Error reading " << _url << ": " << error; QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); From 3215957398c554376a808bd545f1335b4398c9af Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 4 Dec 2014 12:31:40 -0800 Subject: [PATCH 25/29] better interpretation of the hifi light intensity and fixes for compilation on mac --- libraries/fbx/src/FBXReader.cpp | 9 ++++++--- libraries/fbx/src/FBXReader.h | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 80d8660b9f..827af802e5 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1200,11 +1200,11 @@ FBXLight extractLight(const FBXNode& object) { FBXLight light; foreach (const FBXNode& subobject, object.children) { - std::string childname = subobject.name; + std::string childname = QString(subobject.name).toStdString(); if (subobject.name == "Properties70") { foreach (const FBXNode& property, subobject.children) { int valIndex = 4; - std::string propName = property.name; + std::string propName = QString(property.name).toStdString(); if (property.name == "P") { std::string propname = property.properties.at(0).toString().toStdString(); if (propname == "Intensity") { @@ -1784,6 +1784,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, auto lit = lights.find(childID.toStdString()); if (lit != lights.end()) { lightmapLevel = (*lit).second.intensity; + if (lightmapLevel <= 0.0f) { + loadLightmaps = false; + } } } } @@ -2094,7 +2097,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, emissiveParams.y = lightmapLevel; QString emissiveTextureID = emissiveTextures.value(childID); QString ambientTextureID = ambientTextures.value(childID); - if (!emissiveTextureID.isNull() || !ambientTextureID.isNull()) { + if (loadLightmaps && (!emissiveTextureID.isNull() || !ambientTextureID.isNull())) { if (!emissiveTextureID.isNull()) { emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index aacfbc0a98..c34a9677a6 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -172,6 +172,13 @@ public: Transform transform; float intensity; glm::vec3 color; + + FBXLight() : + name(), + transform(), + intensity(1.0f), + color(1.0f) + {} }; Q_DECLARE_METATYPE(FBXAnimationFrame) From 4168993ebe2415e83bf6cf499b24d69b60ec42cb Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 4 Dec 2014 14:23:47 -0800 Subject: [PATCH 26/29] Replace c++11 usage of auto by explicit declaration --- libraries/fbx/src/FBXReader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 827af802e5..a750ab53e2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1781,7 +1781,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) { - auto lit = lights.find(childID.toStdString()); + std::map< std::string, FBXLight >::iterator lit = lights.find(childID.toStdString()); if (lit != lights.end()) { lightmapLevel = (*lit).second.intensity; if (lightmapLevel <= 0.0f) { @@ -1856,8 +1856,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, // TODO: check if is code is needed if (!lights.empty()) { if (hifiGlobalNodeID.isEmpty()) { - auto l = (*lights.begin()); - lightmapLevel = (l.second).intensity; + std::map< std::string, FBXLight >::iterator l = lights.begin(); + lightmapLevel = (*l).second.intensity; } } From 9819658e202692105b43f1fbc650ca9f382c3a4f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Dec 2014 16:41:19 -0800 Subject: [PATCH 27/29] Spanner subdivision streaming bits. --- .../metavoxels/src/AttributeRegistry.cpp | 61 ++++++++++++++----- libraries/metavoxels/src/Bitstream.cpp | 14 ++++- libraries/metavoxels/src/Bitstream.h | 10 ++- libraries/metavoxels/src/SharedObject.cpp | 4 +- libraries/metavoxels/src/SharedObject.h | 9 +-- libraries/metavoxels/src/Spanner.cpp | 8 ++- libraries/metavoxels/src/Spanner.h | 4 +- 7 files changed, 81 insertions(+), 29 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index fbc7f0d110..afdc0c923f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1274,25 +1274,37 @@ void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, Metavoxe void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) { - forever { - SharedObjectPointer object; - state.base.stream >> object; - if (!object) { - break; + readMetavoxelSubdivision(data, state); +} + +static void writeDeltaSubdivision(SharedObjectSet& oldSet, SharedObjectSet& newSet, Bitstream& stream) { + for (SharedObjectSet::iterator newIt = newSet.begin(); newIt != newSet.end(); ) { + SharedObjectSet::iterator oldIt = oldSet.find(*newIt); + if (oldIt == oldSet.end()) { + stream << *newIt; // added + newIt = newSet.erase(newIt); + + } else { + oldSet.erase(oldIt); + newIt++; } - data.toggle(state.base.attribute, object); } - // even if the root is empty, it should still exist - if (!data.getRoot(state.base.attribute)) { - data.createRoot(state.base.attribute); + foreach (const SharedObjectPointer& object, oldSet) { + stream << object; // removed } + stream << SharedObjectPointer(); + foreach (const SharedObjectPointer& object, newSet) { + object->maybeWriteSubdivision(stream); + } + stream << SharedObjectPointer(); } void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) { - state.base.visit = Spanner::getAndIncrementNextVisit(); - root.writeSpannerDelta(reference, state); - state.base.stream << SharedObjectPointer(); + SharedObjectSet oldSet, newSet; + reference.getSpanners(this, state.minimum, state.size, state.base.referenceLOD, oldSet); + root.getSpanners(this, state.minimum, state.size, state.base.lod, newSet); + writeDeltaSubdivision(oldSet, newSet, state.base.stream); } void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { @@ -1302,14 +1314,31 @@ void SpannerSetAttribute::readMetavoxelSubdivision(MetavoxelData& data, Metavoxe if (!object) { break; } - data.insert(state.base.attribute, object); + data.toggle(state.base.attribute, object); + } + forever { + SharedObjectPointer object; + state.base.stream >> object; + if (!object) { + break; + } + SharedObjectPointer newObject = object->readSubdivision(state.base.stream); + if (newObject != object) { + data.replace(state.base.attribute, object, newObject); + state.base.stream.addSubdividedObject(newObject); + } + } + // even if the root is empty, it should still exist + if (!data.getRoot(state.base.attribute)) { + data.createRoot(state.base.attribute); } } void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { - state.base.visit = Spanner::getAndIncrementNextVisit(); - root.writeSpannerSubdivision(state); - state.base.stream << SharedObjectPointer(); + SharedObjectSet oldSet, newSet; + root.getSpanners(this, state.minimum, state.size, state.base.referenceLOD, oldSet); + root.getSpanners(this, state.minimum, state.size, state.base.lod, newSet); + writeDeltaSubdivision(oldSet, newSet, state.base.stream); } bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e4af43feee..c3bd05d3c7 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -267,7 +267,9 @@ Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { _typeStreamerStreamer.getAndResetTransientValues(), _attributeStreamer.getAndResetTransientValues(), _scriptStringStreamer.getAndResetTransientValues(), - _sharedObjectStreamer.getAndResetTransientValues() }; + _sharedObjectStreamer.getAndResetTransientValues(), + _subdividedObjects }; + _subdividedObjects.clear(); return mappings; } @@ -291,6 +293,16 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { reference = it.value(); _weakSharedObjectHash.remove(it.value()->getRemoteID()); } + foreach (const SharedObjectPointer& object, mappings.subdividedObjects) { + QPointer& reference = _sharedObjectReferences[object->getRemoteOriginID()]; + if (reference && reference != object) { + int id = _sharedObjectStreamer.removePersistentValue(reference.data()); + if (id != 0) { + _sharedObjectStreamer.insertPersistentValue(id, object); + } + } + reference = object; + } } void Bitstream::persistAndResetReadMappings() { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index e5aa30fac5..121aa6c672 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -99,10 +99,12 @@ public: int takePersistentID(P value) { return _persistentIDs.take(value); } - void removePersistentValue(V value) { int id = _valueIDs.take(value); _persistentValues.remove(id); } + int removePersistentValue(V value) { int id = _valueIDs.take(value); _persistentValues.remove(id); return id; } V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; } + void insertPersistentValue(int id, V value) { _valueIDs.insert(value, id); _persistentValues.insert(id, value); } + void copyPersistentMappings(const RepeatedValueStreamer& other); void clearPersistentMappings(); @@ -289,6 +291,7 @@ public: QHash attributeValues; QHash scriptStringValues; QHash sharedObjectValues; + QVector subdividedObjects; }; /// Performs all of the various lazily initializations (of object streamers, etc.) If multiple threads need to use @@ -374,6 +377,9 @@ public: /// Resets to the initial state. void reset(); + /// Adds a subdivided object, which will be added to the read mappings and used as a reference if persisted. + void addSubdividedObject(const SharedObjectPointer& object) { _subdividedObjects.append(object); } + /// Returns the set of transient mappings gathered during writing and resets them. WriteMappings getAndResetWriteMappings(); @@ -576,6 +582,8 @@ private: RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; + QVector _subdividedObjects; + WeakSharedObjectHash _sharedObjectReferences; WeakSharedObjectHash _weakSharedObjectHash; diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 95dc75fec0..dcfa9732b3 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -147,11 +147,11 @@ void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) // nothing by default } -void SharedObject::writeExtraSubdivision(Bitstream& out) { +void SharedObject::maybeWriteSubdivision(Bitstream& out) { // nothing by default } -SharedObject* SharedObject::readExtraSubdivision(Bitstream& in) { +SharedObject* SharedObject::readSubdivision(Bitstream& in) { return this; } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index cf9bf4e645..ebea322bf1 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -92,12 +92,13 @@ public: /// Reads the delta-encoded non-property contents of this object from the specified stream. virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); - /// Writes the subdivision of the non-property contents of this object to the specified stream. - virtual void writeExtraSubdivision(Bitstream& out); + /// Writes the subdivision of the contents of this object (preceeded by a + /// reference to the object itself) to the specified stream if necessary. + virtual void maybeWriteSubdivision(Bitstream& out); - /// Reads the subdivision of the non-property contents of this object from the specified stream. + /// Reads the subdivision of this object from the specified stream. /// \return the modified object, or this if no modification was performed - virtual SharedObject* readExtraSubdivision(Bitstream& in); + virtual SharedObject* readSubdivision(Bitstream& in); private: diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index f2a1ebb27f..1dc55a7904 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -3016,7 +3016,7 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { } } -void Heightfield::writeExtraSubdivision(Bitstream& out) { +void Heightfield::maybeWriteSubdivision(Bitstream& out) { MetavoxelLOD lod, referenceLOD; if (out.getContext()) { MetavoxelStreamBase* base = static_cast(out.getContext()); @@ -3026,13 +3026,13 @@ void Heightfield::writeExtraSubdivision(Bitstream& out) { HeightfieldStreamBase base = { out, lod, referenceLOD }; HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - if (state.becameSubdivided()) { + if (state.becameSubdividedOrCollapsed()) { out << SharedObjectPointer(this); _root->writeSubdivision(state); } } -SharedObject* Heightfield::readExtraSubdivision(Bitstream& in) { +SharedObject* Heightfield::readSubdivision(Bitstream& in) { MetavoxelLOD lod, referenceLOD; if (in.getContext()) { MetavoxelStreamBase* base = static_cast(in.getContext()); @@ -3046,6 +3046,8 @@ SharedObject* Heightfield::readExtraSubdivision(Bitstream& in) { HeightfieldNodePointer root(_root->readSubdivision(state)); if (_root != root) { Heightfield* newHeightfield = static_cast(clone(true)); + newHeightfield->setRemoteID(getRemoteID()); + newHeightfield->setRemoteOriginID(getRemoteOriginID()); newHeightfield->setRoot(root); return newHeightfield; } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index bbfcbff1ff..bec1355b48 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -642,8 +642,8 @@ public: virtual void readExtra(Bitstream& in); virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); - virtual void writeExtraSubdivision(Bitstream& out); - virtual SharedObject* readExtraSubdivision(Bitstream& in); + virtual void maybeWriteSubdivision(Bitstream& out); + virtual SharedObject* readSubdivision(Bitstream& in); signals: From 466d99bcbb89f20d8e4c683eeb5a5defd4101348 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Dec 2014 17:12:06 -0800 Subject: [PATCH 28/29] Removed unused members. --- interface/src/gpu/GLBackend.cpp | 1 - libraries/fbx/src/FBXReader.cpp | 1 - libraries/metavoxels/src/Spanner.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/interface/src/gpu/GLBackend.cpp b/interface/src/gpu/GLBackend.cpp index 1f8e7bf99f..10567e65ba 100644 --- a/interface/src/gpu/GLBackend.cpp +++ b/interface/src/gpu/GLBackend.cpp @@ -445,7 +445,6 @@ void GLBackend::updateTransform() { _transform._lastMode = GL_PROJECTION; } CHECK_GL_ERROR();*/ - _transform._invalidProj; } if (_transform._invalidModel || _transform._invalidView) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5f215ac4d0..289051eafb 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1477,7 +1477,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } else if (object.name == "Texture") { TextureParam tex; - bool texparam = false; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { // trim off any path information diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 1dc55a7904..216d4d7d1d 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -1127,7 +1127,6 @@ bool HeightfieldStreamState::becameSubdividedOrCollapsed() const { const int X_MAXIMUM_FLAG = 1; const int Y_MAXIMUM_FLAG = 2; -const int MAXIMUM_FLAG_MASK = X_MAXIMUM_FLAG | Y_MAXIMUM_FLAG; static glm::vec2 getNextMinimum(const glm::vec2& minimum, float nextSize, int index) { return minimum + glm::vec2( From dff22b01b6757cae80d233b4ba1fd66a20f386f1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Dec 2014 01:58:45 -0800 Subject: [PATCH 29/29] Allow loading "MDR" files saved by Wilbur, fix for reallocating error. --- libraries/metavoxels/src/Spanner.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 216d4d7d1d..956dc45729 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -608,22 +608,27 @@ static int getHeightfieldSize(int size) { void HeightfieldHeightEditor::select() { QSettings settings; QString result = QFileDialog::getOpenFileName(this, "Select Height Image", settings.value("heightDir").toString(), - "Images (*.png *.jpg *.bmp *.raw)"); + "Images (*.png *.jpg *.bmp *.raw *.mdr)"); if (result.isNull()) { return; } settings.setValue("heightDir", QFileInfo(result).path()); const quint16 CONVERSION_OFFSET = 1; - if (result.toLower().endsWith(".raw")) { + QString lowerResult = result.toLower(); + bool isMDR = lowerResult.endsWith(".mdr"); + if (lowerResult.endsWith(".raw") || isMDR) { QFile input(result); input.open(QIODevice::ReadOnly); QDataStream in(&input); in.setByteOrder(QDataStream::LittleEndian); - QVector rawContents; - while (!in.atEnd()) { - quint16 height; - in >> height; - rawContents.append(height); + if (isMDR) { + const int MDR_HEADER_SIZE = 1024; + input.seek(MDR_HEADER_SIZE); + } + int available = input.bytesAvailable() / sizeof(quint16); + QVector rawContents(available); + for (quint16* height = rawContents.data(), *end = height + available; height != end; height++) { + in >> *height; } if (rawContents.isEmpty()) { QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");