From 28f9af695e38a25ba7002e8c4e9600a05a288cc2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 25 Aug 2014 11:34:41 -0700 Subject: [PATCH 01/51] Load splat texture as such when painting. --- interface/src/ui/MetavoxelEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index ce09e3657d..430bdf3148 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1197,5 +1197,5 @@ QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { void HeightfieldTextureBrushTool::updateTexture() { HeightfieldTexture* texture = static_cast(_textureEditor->getObject().data()); - _texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL()); + _texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL(), SPLAT_TEXTURE); } From 7aa2a1d53f9e8db578d9255b78943f5eb19fac5d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 25 Aug 2014 15:44:12 -0700 Subject: [PATCH 02/51] Working on voxel representations. --- interface/src/MetavoxelSystem.cpp | 32 +++--- interface/src/ui/MetavoxelEditor.cpp | 14 +-- .../metavoxels/src/AttributeRegistry.cpp | 105 ++++++++++++------ libraries/metavoxels/src/AttributeRegistry.h | 68 ++++++++---- .../metavoxels/src/MetavoxelMessages.cpp | 8 +- 5 files changed, 149 insertions(+), 78 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 1d97dc94fc..00047bbfbe 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -623,7 +623,7 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _textureTextureID(0), _heightSize(glm::sqrt(height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), - _colorSize(glm::sqrt(color.size() / HeightfieldData::COLOR_BYTES)), + _colorSize(glm::sqrt(color.size() / DataBlock::COLOR_BYTES)), _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; @@ -660,13 +660,13 @@ QByteArray HeightfieldBuffer::getUnextendedHeight() const { } QByteArray HeightfieldBuffer::getUnextendedColor() const { - int srcSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES); + int srcSize = glm::sqrt(_color.size() / DataBlock::COLOR_BYTES); int destSize = srcSize - 1; - QByteArray unextended(destSize * destSize * HeightfieldData::COLOR_BYTES, 0); + QByteArray unextended(destSize * destSize * DataBlock::COLOR_BYTES, 0); const char* src = _color.constData(); - int srcStride = srcSize * HeightfieldData::COLOR_BYTES; + int srcStride = srcSize * DataBlock::COLOR_BYTES; char* dest = unextended.data(); - int destStride = destSize * HeightfieldData::COLOR_BYTES; + int destStride = destSize * DataBlock::COLOR_BYTES; for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) { memcpy(dest, src, destStride); } @@ -705,7 +705,7 @@ void HeightfieldBuffer::render(bool cursor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR); } else { - int colorSize = glm::sqrt(_color.size() / HeightfieldData::COLOR_BYTES); + int colorSize = glm::sqrt(_color.size() / DataBlock::COLOR_BYTES); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); } @@ -1289,13 +1289,13 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); - dest = _buffer->getColor().data() + (destY * colorSize + destX) * HeightfieldData::COLOR_BYTES; - int destStride = colorSize * HeightfieldData::COLOR_BYTES; - int destBytes = destWidth * HeightfieldData::COLOR_BYTES; + dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; + int destStride = colorSize * DataBlock::COLOR_BYTES; + int destBytes = destWidth * DataBlock::COLOR_BYTES; const QByteArray& srcColor = color->getContents(); - srcSize = glm::sqrt(srcColor.size() / HeightfieldData::COLOR_BYTES); - int srcStride = srcSize * HeightfieldData::COLOR_BYTES; + srcSize = glm::sqrt(srcColor.size() / DataBlock::COLOR_BYTES); + int srcStride = srcSize * DataBlock::COLOR_BYTES; srcIncrement = info.size / srcSize; if (srcIncrement == colorIncrement) { @@ -1303,7 +1303,7 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - const char* src = srcColor.constData() + (srcY * srcSize + srcX) * HeightfieldData::COLOR_BYTES; + const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES; for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { memcpy(dest, src, destBytes); } @@ -1315,9 +1315,9 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { const char* src = srcColor.constData() + (int)srcY * srcStride; float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += HeightfieldData::COLOR_BYTES, + for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES, lineSrcX += srcAdvance) { - const char* lineSrc = src + (int)lineSrcX * HeightfieldData::COLOR_BYTES; + const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; lineDest[0] = lineSrc[0]; lineDest[1] = lineSrc[1]; lineDest[2] = lineSrc[2]; @@ -1372,9 +1372,9 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { int colorContentsSize = 0; if (color) { const QByteArray& colorContents = color->getContents(); - int colorSize = glm::sqrt(colorContents.size() / HeightfieldData::COLOR_BYTES); + int colorSize = glm::sqrt(colorContents.size() / DataBlock::COLOR_BYTES); int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; - colorContentsSize = extendedColorSize * extendedColorSize * HeightfieldData::COLOR_BYTES; + colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; } HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 430bdf3148..fcc5ec0b1a 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -966,7 +966,7 @@ void ImportHeightfieldTool::apply() { QByteArray color; if (buffer->getColor().isEmpty()) { const int WHITE_VALUE = 0xFF; - color = QByteArray(height.size() * HeightfieldData::COLOR_BYTES, WHITE_VALUE); + color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); } else { color = buffer->getUnextendedColor(); } @@ -1032,22 +1032,22 @@ void ImportHeightfieldTool::updatePreview() { int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI); int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ); for (int y = 0; y < rows; y++) { - uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES; + uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES; char* dest = height.data() + (y + offsetY) * heightSize + offsetX; for (int x = 0; x < columns; x++) { *dest++ = *src; - src += HeightfieldData::COLOR_BYTES; + src += DataBlock::COLOR_BYTES; } } QByteArray color; if (!_colorImage.isNull()) { - color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0); + color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0); rows = qMax(0, qMin(colorSize, _colorImage.height() - i)); columns = qMax(0, qMin(colorSize, _colorImage.width() - j)); for (int y = 0; y < rows; y++) { - memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES, - _colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES, - columns * HeightfieldData::COLOR_BYTES); + memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES, + _colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES, + columns * DataBlock::COLOR_BYTES); } } buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color))); diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 425bf8ff4a..97e6beec3b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -497,11 +497,7 @@ AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& paren return AttributeValue(parentValue.getAttribute()); } -HeightfieldData::HeightfieldData(const QByteArray& contents) : - _contents(contents) { -} - -HeightfieldData::~HeightfieldData() { +DataBlock::~DataBlock() { } enum HeightfieldImage { NULL_HEIGHTFIELD_IMAGE, NORMAL_HEIGHTFIELD_IMAGE, DEFLATED_HEIGHTFIELD_IMAGE }; @@ -548,7 +544,7 @@ const QImage decodeHeightfieldImage(const QByteArray& data) { } HeightfieldHeightData::HeightfieldHeightData(const QByteArray& contents) : - HeightfieldData(contents) { + _contents(contents) { } HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes) { @@ -562,7 +558,7 @@ HeightfieldHeightData::HeightfieldHeightData(Bitstream& in, int bytes, const Hei } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); if (image.isNull()) { @@ -685,7 +681,7 @@ void HeightfieldHeightData::writeDelta(Bitstream& out, const HeightfieldHeightDa } image.setOffset(QPoint(minX + 1, minY + 1)); reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); @@ -744,7 +740,7 @@ void HeightfieldHeightData::set(const QImage& image) { } HeightfieldColorData::HeightfieldColorData(const QByteArray& contents) : - HeightfieldData(contents) { + _contents(contents) { } HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes) { @@ -758,7 +754,7 @@ HeightfieldColorData::HeightfieldColorData(Bitstream& in, int bytes, const Heigh } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); QImage image = decodeHeightfieldImage(reference->getEncodedDelta()); if (image.isNull()) { @@ -875,7 +871,7 @@ void HeightfieldColorData::writeDelta(Bitstream& out, const HeightfieldColorData } image.setOffset(QPoint(minX + 1, minY + 1)); reference->setEncodedDelta(encodeHeightfieldImage(image)); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); @@ -954,7 +950,7 @@ static QByteArray decodeTexture(const QByteArray& encoded, int& offsetX, int& of } HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector& textures) : - HeightfieldData(contents), + _contents(contents), _textures(textures) { } @@ -970,7 +966,7 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); in.readDelta(_textures, reference->getTextures()); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); int offsetX, offsetY, width, height; @@ -1042,7 +1038,7 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture } } reference->setEncodedDelta(encodeTexture(minX + 1, minY + 1, width, height, delta)); - reference->setDeltaData(HeightfieldDataPointer(this)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); @@ -1251,8 +1247,8 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post *(HeightfieldColorDataPointer*)&parent = HeightfieldColorDataPointer(); return true; } - int size = glm::sqrt(maxSize / (float)HeightfieldData::COLOR_BYTES); - QByteArray contents(size * size * HeightfieldData::COLOR_BYTES, 0); + int size = glm::sqrt(maxSize / (float)DataBlock::COLOR_BYTES); + QByteArray contents(size * size * DataBlock::COLOR_BYTES, 0); int halfSize = size / 2; for (int i = 0; i < MERGE_COUNT; i++) { HeightfieldColorDataPointer child = decodeInline(children[i]); @@ -1260,7 +1256,7 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post continue; } const QByteArray& childContents = child->getContents(); - int childSize = glm::sqrt(childContents.size() / (float)HeightfieldData::COLOR_BYTES); + int childSize = glm::sqrt(childContents.size() / (float)DataBlock::COLOR_BYTES); const int INDEX_MASK = 1; int xIndex = i & INDEX_MASK; const int Y_SHIFT = 1; @@ -1270,24 +1266,24 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post } int Z_SHIFT = 2; int zIndex = (i >> Z_SHIFT) & INDEX_MASK; - char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * HeightfieldData::COLOR_BYTES; + char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * DataBlock::COLOR_BYTES; uchar* src = (uchar*)childContents.data(); - int childStride = childSize * HeightfieldData::COLOR_BYTES; - int stride = size * HeightfieldData::COLOR_BYTES; + int childStride = childSize * DataBlock::COLOR_BYTES; + int stride = size * DataBlock::COLOR_BYTES; int halfStride = stride / 2; - int childStep = 2 * HeightfieldData::COLOR_BYTES; - int redOffset3 = childStride + HeightfieldData::COLOR_BYTES; - int greenOffset1 = HeightfieldData::COLOR_BYTES + 1; + int childStep = 2 * DataBlock::COLOR_BYTES; + int redOffset3 = childStride + DataBlock::COLOR_BYTES; + int greenOffset1 = DataBlock::COLOR_BYTES + 1; int greenOffset2 = childStride + 1; - int greenOffset3 = childStride + HeightfieldData::COLOR_BYTES + 1; - int blueOffset1 = HeightfieldData::COLOR_BYTES + 2; + int greenOffset3 = childStride + DataBlock::COLOR_BYTES + 1; + int blueOffset1 = DataBlock::COLOR_BYTES + 2; int blueOffset2 = childStride + 2; - int blueOffset3 = childStride + HeightfieldData::COLOR_BYTES + 2; + int blueOffset3 = childStride + DataBlock::COLOR_BYTES + 2; if (childSize == size) { // simple case: one destination value for four child values for (int z = 0; z < halfSize; z++) { - for (char* end = dest + halfSize * HeightfieldData::COLOR_BYTES; dest != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] + + for (char* end = dest + halfSize * DataBlock::COLOR_BYTES; dest != end; src += childStep) { + *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + (int)src[childStride] + (int)src[redOffset3]) >> 2; *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; @@ -1300,14 +1296,14 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post int halfChildSize = childSize / 2; int destPerSrc = size / childSize; for (int z = 0; z < halfChildSize; z++) { - for (uchar* end = src + childSize * HeightfieldData::COLOR_BYTES; src != end; src += childStep) { - *dest++ = ((int)src[0] + (int)src[HeightfieldData::COLOR_BYTES] + + for (uchar* end = src + childSize * DataBlock::COLOR_BYTES; src != end; src += childStep) { + *dest++ = ((int)src[0] + (int)src[DataBlock::COLOR_BYTES] + (int)src[childStride] + (int)src[redOffset3]) >> 2; *dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2; *dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2; for (int j = 1; j < destPerSrc; j++) { - memcpy(dest, dest - HeightfieldData::COLOR_BYTES, HeightfieldData::COLOR_BYTES); - dest += HeightfieldData::COLOR_BYTES; + memcpy(dest, dest - DataBlock::COLOR_BYTES, DataBlock::COLOR_BYTES); + dest += DataBlock::COLOR_BYTES; } } dest += halfStride; @@ -1390,6 +1386,51 @@ bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool po return maxSize == 0; } +VoxelSignData::VoxelSignData(const QByteArray& contents) : + _contents(contents) { +} + +VoxelSignData::VoxelSignData(Bitstream& in, int bytes) { + read(in, bytes); +} + +VoxelSignData::VoxelSignData(Bitstream& in, int bytes, const VoxelSignDataPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _contents = reference->getContents(); +} + +void VoxelSignData::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void VoxelSignData::writeDelta(Bitstream& out, const VoxelSignDataPointer& reference) { + if (!reference || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void VoxelSignData::read(Bitstream& in, int bytes) { + +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 66da7a9b6f..deb9246b53 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -29,14 +29,15 @@ class QScriptEngine; class QScriptValue; class Attribute; +class DataBlock; class HeightfieldColorData; -class HeightfieldData; class HeightfieldHeightData; class HeightfieldTextureData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; class MetavoxelStreamState; +class VoxelSignData; typedef SharedObjectPointerTemplate AttributePointer; @@ -100,13 +101,13 @@ public: /// Returns a reference to the standard "spannerMask" attribute. const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; } - /// Returns a reference to the standard HeightfieldDataPointer "heightfield" attribute. + /// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute. const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; } - /// Returns a reference to the standard HeightfieldDataPointer "heightfieldColor" attribute. + /// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute. const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; } - /// Returns a reference to the standard HeightfieldDataPointer "heightfieldTexture" attribute. + /// Returns a reference to the standard HeightfieldTextureDataPointer "heightfieldTexture" attribute. const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; } private: @@ -435,21 +436,18 @@ public: virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; -typedef QExplicitlySharedDataPointer HeightfieldDataPointer; +typedef QExplicitlySharedDataPointer DataBlockPointer; -/// Contains a block of heightfield data. -class HeightfieldData : public QSharedData { +/// Base class for blocks of data. +class DataBlock : public QSharedData { public: static const int COLOR_BYTES = 3; - HeightfieldData(const QByteArray& contents = QByteArray()); - virtual ~HeightfieldData(); + virtual ~DataBlock(); - const QByteArray& getContents() const { return _contents; } - - void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; } - const HeightfieldDataPointer& getDeltaData() const { return _deltaData; } + void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; } + const DataBlockPointer& getDeltaData() const { return _deltaData; } void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; } const QByteArray& getEncodedDelta() const { return _encodedDelta; } @@ -458,17 +456,16 @@ public: protected: - QByteArray _contents; QByteArray _encoded; QMutex _encodedMutex; - HeightfieldDataPointer _deltaData; + DataBlockPointer _deltaData; QByteArray _encodedDelta; QMutex _encodedDeltaMutex; class EncodedSubdivision { public: - HeightfieldDataPointer ancestor; + DataBlockPointer ancestor; QByteArray data; }; QVector _encodedSubdivisions; @@ -478,7 +475,7 @@ protected: typedef QExplicitlySharedDataPointer HeightfieldHeightDataPointer; /// Contains a block of heightfield height data. -class HeightfieldHeightData : public HeightfieldData { +class HeightfieldHeightData : public DataBlock { public: HeightfieldHeightData(const QByteArray& contents); @@ -487,6 +484,8 @@ public: HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor, const glm::vec3& minimum, float size); + const QByteArray& getContents() const { return _contents; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference); void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor, @@ -496,12 +495,14 @@ private: void read(Bitstream& in, int bytes); void set(const QImage& image); + + QByteArray _contents; }; typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; /// Contains a block of heightfield color data. -class HeightfieldColorData : public HeightfieldData { +class HeightfieldColorData : public DataBlock { public: HeightfieldColorData(const QByteArray& contents); @@ -510,6 +511,8 @@ public: HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor, const glm::vec3& minimum, float size); + const QByteArray& getContents() const { return _contents; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference); void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor, @@ -519,12 +522,14 @@ private: void read(Bitstream& in, int bytes); void set(const QImage& image); + + QByteArray _contents; }; typedef QExplicitlySharedDataPointer HeightfieldTextureDataPointer; /// Contains a block of heightfield texture data. -class HeightfieldTextureData : public HeightfieldData { +class HeightfieldTextureData : public DataBlock { public: HeightfieldTextureData(const QByteArray& contents, @@ -532,6 +537,8 @@ public: HeightfieldTextureData(Bitstream& in, int bytes); HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference); + const QByteArray& getContents() const { return _contents; } + const QVector& getTextures() const { return _textures; } void write(Bitstream& out); @@ -541,6 +548,7 @@ private: void read(Bitstream& in, int bytes); + QByteArray _contents; QVector _textures; }; @@ -618,6 +626,28 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; +typedef QExplicitlySharedDataPointer VoxelSignDataPointer; + +/// Contains a block of voxel sign data. +class VoxelSignData : public DataBlock { +public: + + VoxelSignData(const QByteArray& contents); + VoxelSignData(Bitstream& in, int bytes); + VoxelSignData(Bitstream& in, int bytes, const VoxelSignDataPointer& reference); + + const QByteArray& getContents() const { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const VoxelSignDataPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QByteArray _contents; +}; + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 1a8f64d935..2109fea369 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -442,7 +442,7 @@ static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position return; } QByteArray contents(pointer->getContents()); - int size = glm::sqrt((float)contents.size() / HeightfieldData::COLOR_BYTES); + int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); int highest = size - 1; float heightScale = size / info.size; @@ -456,14 +456,14 @@ static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position // 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)highest); - int stride = size * HeightfieldData::COLOR_BYTES; - char* lineDest = contents.data() + (int)z * stride + (int)startX * HeightfieldData::COLOR_BYTES; + int stride = size * DataBlock::COLOR_BYTES; + char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; float squaredRadius = scaledRadius * scaledRadius; char red = color.red(), green = color.green(), blue = color.blue(); bool changed = false; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { char* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += HeightfieldData::COLOR_BYTES) { + for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { float dx = x - center.x, dz = z - center.z; if (dx * dx + dz * dz <= squaredRadius) { dest[0] = red; From e454a08e5765877c2a8af26a709d1bad8af4ebe8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 25 Aug 2014 18:57:45 -0700 Subject: [PATCH 03/51] HeightfieldTexture -> MaterialObject (we want to use it for voxels, too, and eventually include components other than diffuse color). --- interface/src/MetavoxelSystem.cpp | 68 +++---- interface/src/MetavoxelSystem.h | 16 +- interface/src/ui/MetavoxelEditor.cpp | 34 ++-- interface/src/ui/MetavoxelEditor.h | 6 +- .../metavoxels/src/AttributeRegistry.cpp | 141 ++++++++----- libraries/metavoxels/src/AttributeRegistry.h | 185 ++++++++++-------- .../metavoxels/src/MetavoxelMessages.cpp | 78 ++++---- libraries/metavoxels/src/MetavoxelMessages.h | 12 +- 8 files changed, 304 insertions(+), 236 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 00047bbfbe..657b018f27 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -608,19 +608,19 @@ const int HeightfieldBuffer::SHARED_EDGE = 1; const int HeightfieldBuffer::HEIGHT_EXTENSION = 2 * HeightfieldBuffer::HEIGHT_BORDER + HeightfieldBuffer::SHARED_EDGE; HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, - const QByteArray& height, const QByteArray& color, const QByteArray& texture, - const QVector& textures) : + const QByteArray& height, const QByteArray& color, const QByteArray& material, + const QVector& materials) : _translation(translation), _scale(scale), _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), _colorBounds(_heightBounds), _height(height), _color(color), - _texture(texture), - _textures(textures), + _material(material), + _materials(materials), _heightTextureID(0), _colorTextureID(0), - _textureTextureID(0), + _materialTextureID(0), _heightSize(glm::sqrt(height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), _colorSize(glm::sqrt(color.size() / DataBlock::COLOR_BYTES)), @@ -639,11 +639,11 @@ HeightfieldBuffer::~HeightfieldBuffer() { // the textures have to be deleted on the main thread (for its opengl context) if (QThread::currentThread() != Application::getInstance()->thread()) { QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures", - Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID), Q_ARG(int, _textureTextureID)); + Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID), Q_ARG(int, _materialTextureID)); } else { glDeleteTextures(1, &_heightTextureID); glDeleteTextures(1, &_colorTextureID); - glDeleteTextures(1, &_textureTextureID); + glDeleteTextures(1, &_materialTextureID); } } @@ -709,23 +709,23 @@ void HeightfieldBuffer::render(bool cursor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData()); } - if (!_texture.isEmpty()) { - glGenTextures(1, &_textureTextureID); - glBindTexture(GL_TEXTURE_2D, _textureTextureID); + if (!_material.isEmpty()) { + 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); - int textureSize = glm::sqrt(_texture.size()); - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, textureSize, textureSize, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, _texture.constData()); + int materialSize = glm::sqrt(_material.size()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, materialSize, materialSize, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, _material.constData()); - _networkTextures.resize(_textures.size()); - for (int i = 0; i < _textures.size(); i++) { - const SharedObjectPointer texture = _textures.at(i); - if (texture) { + _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(texture.data())->getURL(), SPLAT_TEXTURE); + static_cast(material.data())->getDiffuse(), SPLAT_TEXTURE); } } } @@ -799,7 +799,7 @@ void HeightfieldBuffer::render(bool cursor) { if (cursor) { glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - } else if (!_textures.isEmpty()) { + } else if (!_materials.isEmpty()) { DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); @@ -826,10 +826,10 @@ void HeightfieldBuffer::render(bool cursor) { DefaultMetavoxelRendererImplementation::getSplatTextureOffsetLocation(), _translation.x / _scale, _translation.z / _scale); - glBindTexture(GL_TEXTURE_2D, _textureTextureID); + glBindTexture(GL_TEXTURE_2D, _materialTextureID); const int TEXTURES_PER_SPLAT = 4; - for (int i = 0; i < _textures.size(); i += TEXTURES_PER_SPLAT) { + for (int i = 0; i < _materials.size(); i += TEXTURES_PER_SPLAT) { QVector4D scalesS, scalesT; for (int j = 0; j < SPLAT_COUNT; j++) { @@ -838,9 +838,9 @@ void HeightfieldBuffer::render(bool cursor) { if (index < _networkTextures.size()) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { - HeightfieldTexture* heightfieldTexture = static_cast(_textures.at(index).data()); - scalesS[j] = _scale / heightfieldTexture->getScaleS(); - scalesT[j] = _scale / heightfieldTexture->getScaleT(); + MaterialObject* material = static_cast(_materials.at(index).data()); + scalesS[j] = _scale / material->getScaleS(); + scalesT[j] = _scale / material->getScaleT(); glBindTexture(GL_TEXTURE_2D, texture->getID()); } else { glBindTexture(GL_TEXTURE_2D, 0); @@ -1349,7 +1349,7 @@ private: HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << - AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), @@ -1377,12 +1377,12 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; } - HeightfieldTextureDataPointer texture = info.inputValues.at(2).getInlineValue(); - QByteArray textureContents; - QVector textures; - if (texture) { - textureContents = texture->getContents(); - textures = texture->getTextures(); + HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); + QByteArray materialContents; + QVector materials; + if (material) { + materialContents = material->getContents(); + materials = material->getMaterials(); } const HeightfieldBuffer* existingBuffer = static_cast( @@ -1393,12 +1393,12 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { // we already have a buffer of the correct resolution addRegion(bounds, existingBuffer->getHeightBounds()); buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), textureContents, textures); + existingBuffer->getColor(), materialContents, materials); } else { // we must create a new buffer and update its borders buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), textureContents, textures); + QByteArray(colorContentsSize, 0), materialContents, materials); const Box& heightBounds = buffer->getHeightBounds(); addRegion(bounds, heightBounds); @@ -1472,7 +1472,7 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, - buffer->getHeight(), buffer->getColor(), buffer->getTexture(), buffer->getTextures()); + buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials()); _fetchVisitor.init(newBuffer); _data->guide(_fetchVisitor); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(newBuffer))); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index b1ddcf0bff..45496b14a1 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -143,8 +143,8 @@ public: static const int HEIGHT_EXTENSION; HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, - const QByteArray& color, const QByteArray& texture = QByteArray(), - const QVector& textures = QVector()); + const QByteArray& color, const QByteArray& material = QByteArray(), + const QVector& materials = QVector()); ~HeightfieldBuffer(); const glm::vec3& getTranslation() const { return _translation; } @@ -159,10 +159,10 @@ public: QByteArray& getColor() { return _color; } const QByteArray& getColor() const { return _color; } - QByteArray& getTexture() { return _texture; } - const QByteArray& getTexture() const { return _texture; } + QByteArray& getMaterial() { return _material; } + const QByteArray& getMaterial() const { return _material; } - const QVector& getTextures() const { return _textures; } + const QVector& getMaterials() const { return _materials; } QByteArray getUnextendedHeight() const; QByteArray getUnextendedColor() const; @@ -183,11 +183,11 @@ private: Box _colorBounds; QByteArray _height; QByteArray _color; - QByteArray _texture; - QVector _textures; + QByteArray _material; + QVector _materials; GLuint _heightTextureID; GLuint _colorTextureID; - GLuint _textureTextureID; + GLuint _materialTextureID; QVector _networkTextures; int _heightSize; float _heightIncrement; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index fcc5ec0b1a..40e1b75970 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -120,7 +120,7 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new EraseHeightfieldTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldColorBrushTool(this)); - addTool(new HeightfieldTextureBrushTool(this)); + addTool(new HeightfieldMaterialBrushTool(this)); updateAttributes(); @@ -975,10 +975,10 @@ void ImportHeightfieldTool::apply() { AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE; - QByteArray texture(size * size, 0); - HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer)))); + QByteArray material(size * size, 0); + HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue( + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer)))); MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( _translation->getValue() + buffer->getTranslation() * scale, data)) }; @@ -1177,25 +1177,25 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) { alternate ? QColor() : _color->getColor())); } -HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) : - HeightfieldBrushTool(editor, "Texture Brush") { +HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) : + HeightfieldBrushTool(editor, "Material Brush") { - _form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false)); - connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture); + _form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); + connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldMaterialBrushTool::updateTexture); } -QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) { +QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) { if (alternate) { - return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); + return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor())); } else { - SharedObjectPointer texture = _textureEditor->getObject(); - _textureEditor->detachObject(); - return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), texture, + SharedObjectPointer material = _materialEditor->getObject(); + _materialEditor->detachObject(); + return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), material, _texture ? _texture->getAverageColor() : QColor())); } } -void HeightfieldTextureBrushTool::updateTexture() { - HeightfieldTexture* texture = static_cast(_textureEditor->getObject().data()); - _texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL(), SPLAT_TEXTURE); +void HeightfieldMaterialBrushTool::updateTexture() { + MaterialObject* material = static_cast(_materialEditor->getObject().data()); + _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); } diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 7e37b819d7..a739f258cc 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -363,12 +363,12 @@ private: }; /// Allows texturing parts of the heightfield. -class HeightfieldTextureBrushTool : public HeightfieldBrushTool { +class HeightfieldMaterialBrushTool : public HeightfieldBrushTool { Q_OBJECT public: - HeightfieldTextureBrushTool(MetavoxelEditor* editor); + HeightfieldMaterialBrushTool(MetavoxelEditor* editor); protected: @@ -380,7 +380,7 @@ private slots: private: - SharedObjectEditor* _textureEditor; + SharedObjectEditor* _materialEditor; QSharedPointer _texture; }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 97e6beec3b..6d7d27c934 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -23,10 +23,10 @@ REGISTER_META_OBJECT(QRgbAttribute) REGISTER_META_OBJECT(PackedNormalAttribute) REGISTER_META_OBJECT(SpannerQRgbAttribute) REGISTER_META_OBJECT(SpannerPackedNormalAttribute) -REGISTER_META_OBJECT(HeightfieldTexture) +REGISTER_META_OBJECT(MaterialObject) REGISTER_META_OBJECT(HeightfieldAttribute) REGISTER_META_OBJECT(HeightfieldColorAttribute) -REGISTER_META_OBJECT(HeightfieldTextureAttribute) +REGISTER_META_OBJECT(HeightfieldMaterialAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) @@ -52,7 +52,7 @@ AttributeRegistry::AttributeRegistry() : _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))), _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), - _heightfieldTextureAttribute(registerAttribute(new HeightfieldTextureAttribute("heightfieldTexture"))) { + _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))) { // our baseline LOD threshold is for voxels; spanners and heightfields are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -61,7 +61,7 @@ AttributeRegistry::AttributeRegistry() : const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); - _heightfieldTextureAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); + _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -926,10 +926,10 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } -const int TEXTURE_HEADER_SIZE = sizeof(qint32) * 4; +const int MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; -static QByteArray encodeTexture(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(TEXTURE_HEADER_SIZE, 0); +static QByteArray encodeMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(MATERIAL_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; @@ -939,38 +939,38 @@ static QByteArray encodeTexture(int offsetX, int offsetY, int width, int height, return qCompress(inflated); } -static QByteArray decodeTexture(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { +static QByteArray decodeMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { QByteArray inflated = qUncompress(encoded); const qint32* header = (const qint32*)inflated.constData(); offsetX = *header++; offsetY = *header++; width = *header++; height = *header++; - return inflated.mid(TEXTURE_HEADER_SIZE); + return inflated.mid(MATERIAL_HEADER_SIZE); } -HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector& textures) : +HeightfieldMaterialData::HeightfieldMaterialData(const QByteArray& contents, const QVector& materials) : _contents(contents), - _textures(textures) { + _materials(materials) { } -HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes) { +HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes) { read(in, bytes); } -HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference) { +HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference) { if (!reference) { read(in, bytes); return; } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); - in.readDelta(_textures, reference->getTextures()); + in.readDelta(_materials, reference->getMaterials()); reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); int offsetX, offsetY, width, height; - QByteArray delta = decodeTexture(reference->getEncodedDelta(), offsetX, offsetY, width, height); + QByteArray delta = decodeMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); if (delta.isEmpty()) { return; } @@ -988,18 +988,18 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H } } -void HeightfieldTextureData::write(Bitstream& out) { +void HeightfieldMaterialData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { int size = glm::sqrt((float)_contents.size()); - _encoded = encodeTexture(0, 0, size, size, _contents); + _encoded = encodeMaterial(0, 0, size, size, _contents); } out << _encoded.size(); out.writeAligned(_encoded); - out << _textures; + out << _materials; } -void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference) { +void HeightfieldMaterialData::writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference) { if (!reference || reference->getContents().size() != _contents.size()) { write(out); return; @@ -1037,21 +1037,21 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture memcpy(dest, src, width); } } - reference->setEncodedDelta(encodeTexture(minX + 1, minY + 1, width, height, delta)); + reference->setEncodedDelta(encodeMaterial(minX + 1, minY + 1, width, height, delta)); reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); - out.writeDelta(_textures, reference->getTextures()); + out.writeDelta(_materials, reference->getMaterials()); } -void HeightfieldTextureData::read(Bitstream& in, int bytes) { +void HeightfieldMaterialData::read(Bitstream& in, int bytes) { int offsetX, offsetY, width, height; - _contents = decodeTexture(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); - in >> _textures; + _contents = decodeMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); + in >> _materials; } -HeightfieldTexture::HeightfieldTexture() : +MaterialObject::MaterialObject() : _scaleS(1.0f), _scaleT(1.0f) { } @@ -1319,28 +1319,28 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post return false; } -HeightfieldTextureAttribute::HeightfieldTextureAttribute(const QString& name) : - InlineAttribute(name) { +HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) : + InlineAttribute(name) { } -void HeightfieldTextureAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { +void HeightfieldMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { if (!isLeaf) { return; } int size; in >> size; if (size == 0) { - *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(); + *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); } else { - *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(new HeightfieldTextureData(in, size)); + *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData(in, size)); } } -void HeightfieldTextureAttribute::write(Bitstream& out, void* value, bool isLeaf) const { +void HeightfieldMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const { if (!isLeaf) { return; } - HeightfieldTextureDataPointer data = decodeInline(value); + HeightfieldMaterialDataPointer data = decodeInline(value); if (data) { data->write(out); } else { @@ -1348,53 +1348,53 @@ void HeightfieldTextureAttribute::write(Bitstream& out, void* value, bool isLeaf } } -void HeightfieldTextureAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { +void HeightfieldMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { if (!isLeaf) { return; } int size; in >> size; if (size == 0) { - *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(); + *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(); } else { - *(HeightfieldTextureDataPointer*)&value = HeightfieldTextureDataPointer(new HeightfieldTextureData( - in, size, decodeInline(reference))); + *(HeightfieldMaterialDataPointer*)&value = HeightfieldMaterialDataPointer(new HeightfieldMaterialData( + in, size, decodeInline(reference))); } } -void HeightfieldTextureAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { +void HeightfieldMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { if (!isLeaf) { return; } - HeightfieldTextureDataPointer data = decodeInline(value); + HeightfieldMaterialDataPointer data = decodeInline(value); if (data) { - data->writeDelta(out, decodeInline(reference)); + data->writeDelta(out, decodeInline(reference)); } else { out << 0; } } -bool HeightfieldTextureAttribute::merge(void*& parent, void* children[], bool postRead) const { +bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const { int maxSize = 0; for (int i = 0; i < MERGE_COUNT; i++) { - HeightfieldTextureDataPointer pointer = decodeInline(children[i]); + HeightfieldMaterialDataPointer pointer = decodeInline(children[i]); if (pointer) { maxSize = qMax(maxSize, pointer->getContents().size()); } } - *(HeightfieldTextureDataPointer*)&parent = HeightfieldTextureDataPointer(); + *(HeightfieldMaterialDataPointer*)&parent = HeightfieldMaterialDataPointer(); return maxSize == 0; } -VoxelSignData::VoxelSignData(const QByteArray& contents) : +VoxelColorData::VoxelColorData(const QVector& contents) : _contents(contents) { } -VoxelSignData::VoxelSignData(Bitstream& in, int bytes) { +VoxelColorData::VoxelColorData(Bitstream& in, int bytes) { read(in, bytes); } -VoxelSignData::VoxelSignData(Bitstream& in, int bytes, const VoxelSignDataPointer& reference) { +VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference) { if (!reference) { read(in, bytes); return; @@ -1405,7 +1405,7 @@ VoxelSignData::VoxelSignData(Bitstream& in, int bytes, const VoxelSignDataPointe _contents = reference->getContents(); } -void VoxelSignData::write(Bitstream& out) { +void VoxelColorData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { @@ -1414,7 +1414,7 @@ void VoxelSignData::write(Bitstream& out) { out.writeAligned(_encoded); } -void VoxelSignData::writeDelta(Bitstream& out, const VoxelSignDataPointer& reference) { +void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) { if (!reference || reference->getContents().size() != _contents.size()) { write(out); return; @@ -1427,7 +1427,52 @@ void VoxelSignData::writeDelta(Bitstream& out, const VoxelSignDataPointer& refer out.writeAligned(reference->getEncodedDelta()); } -void VoxelSignData::read(Bitstream& in, int bytes) { +void VoxelColorData::read(Bitstream& in, int bytes) { + +} + +VoxelMaterialData::VoxelMaterialData(const QByteArray& contents) : + _contents(contents) { +} + +VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) { + read(in, bytes); +} + +VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _contents = reference->getContents(); +} + +void VoxelMaterialData::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) { + if (!reference || reference->getContents().size() != _contents.size()) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void VoxelMaterialData::read(Bitstream& in, int bytes) { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index deb9246b53..d7b6575a63 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -32,12 +32,13 @@ class Attribute; class DataBlock; class HeightfieldColorData; class HeightfieldHeightData; -class HeightfieldTextureData; +class HeightfieldMaterialData; class MetavoxelData; class MetavoxelLOD; class MetavoxelNode; class MetavoxelStreamState; -class VoxelSignData; +class VoxelColorData; +class VoxelMaterialData; typedef SharedObjectPointerTemplate AttributePointer; @@ -107,8 +108,8 @@ public: /// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute. const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; } - /// Returns a reference to the standard HeightfieldTextureDataPointer "heightfieldTexture" attribute. - const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; } + /// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute. + const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; } private: @@ -127,7 +128,7 @@ private: AttributePointer _spannerMaskAttribute; AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; - AttributePointer _heightfieldTextureAttribute; + AttributePointer _heightfieldMaterialAttribute; }; /// Converts a value to a void pointer. @@ -499,6 +500,23 @@ private: QByteArray _contents; }; +/// An attribute that stores heightfield data. +class HeightfieldAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE HeightfieldAttribute(const QString& name = QString()); + + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; /// Contains a block of heightfield color data. @@ -526,72 +544,6 @@ private: QByteArray _contents; }; -typedef QExplicitlySharedDataPointer HeightfieldTextureDataPointer; - -/// Contains a block of heightfield texture data. -class HeightfieldTextureData : public DataBlock { -public: - - HeightfieldTextureData(const QByteArray& contents, - const QVector& textures = QVector()); - HeightfieldTextureData(Bitstream& in, int bytes); - HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference); - - const QByteArray& getContents() const { return _contents; } - - const QVector& getTextures() const { return _textures; } - - void write(Bitstream& out); - void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference); - -private: - - void read(Bitstream& in, int bytes); - - QByteArray _contents; - QVector _textures; -}; - -/// Contains the description of a heightfield texture. -class HeightfieldTexture : public SharedObject { - Q_OBJECT - Q_PROPERTY(QUrl url MEMBER _url) - Q_PROPERTY(float scaleS MEMBER _scaleS) - Q_PROPERTY(float scaleT MEMBER _scaleT) - -public: - - Q_INVOKABLE HeightfieldTexture(); - - const QUrl& getURL() const { return _url; } - - float getScaleS() const { return _scaleS; } - float getScaleT() const { return _scaleT; } - -private: - - QUrl _url; - float _scaleS; - float _scaleT; -}; - -/// An attribute that stores heightfield data. -class HeightfieldAttribute : public InlineAttribute { - Q_OBJECT - -public: - - Q_INVOKABLE HeightfieldAttribute(const QString& name = QString()); - - virtual void read(Bitstream& in, void*& value, bool isLeaf) const; - virtual void write(Bitstream& out, void* value, bool isLeaf) const; - - virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; - virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; - - virtual bool merge(void*& parent, void* children[], bool postRead = false) const; -}; - /// An attribute that stores heightfield colors. class HeightfieldColorAttribute : public InlineAttribute { Q_OBJECT @@ -609,13 +561,62 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; -/// An attribute that stores heightfield textures. -class HeightfieldTextureAttribute : public InlineAttribute { +typedef QExplicitlySharedDataPointer HeightfieldMaterialDataPointer; + +/// Contains a block of heightfield material data. +class HeightfieldMaterialData : public DataBlock { +public: + + HeightfieldMaterialData(const QByteArray& contents, + const QVector& materials = QVector()); + HeightfieldMaterialData(Bitstream& in, int bytes); + HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference); + + const QByteArray& getContents() const { return _contents; } + + const QVector& getMaterials() const { return _materials; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QByteArray _contents; + QVector _materials; +}; + +/// Contains the description of a material. +class MaterialObject : public SharedObject { + Q_OBJECT + Q_PROPERTY(QUrl diffuse MEMBER _diffuse) + Q_PROPERTY(float scaleS MEMBER _scaleS) + Q_PROPERTY(float scaleT MEMBER _scaleT) + +public: + + Q_INVOKABLE MaterialObject(); + + const QUrl& getDiffuse() const { return _diffuse; } + + float getScaleS() const { return _scaleS; } + float getScaleT() const { return _scaleT; } + +private: + + QUrl _diffuse; + float _scaleS; + float _scaleT; +}; + +/// An attribute that stores heightfield materials. +class HeightfieldMaterialAttribute : public InlineAttribute { Q_OBJECT public: - Q_INVOKABLE HeightfieldTextureAttribute(const QString& name = QString()); + Q_INVOKABLE HeightfieldMaterialAttribute(const QString& name = QString()); virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; @@ -626,20 +627,42 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; -typedef QExplicitlySharedDataPointer VoxelSignDataPointer; +typedef QExplicitlySharedDataPointer VoxelColorDataPointer; -/// Contains a block of voxel sign data. -class VoxelSignData : public DataBlock { +/// Contains a block of voxel color data. +class VoxelColorData : public DataBlock { public: - VoxelSignData(const QByteArray& contents); - VoxelSignData(Bitstream& in, int bytes); - VoxelSignData(Bitstream& in, int bytes, const VoxelSignDataPointer& reference); + VoxelColorData(const QVector& contents); + VoxelColorData(Bitstream& in, int bytes); + VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference); + + const QVector& getContents() const { return _contents; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QVector _contents; +}; + +typedef QExplicitlySharedDataPointer VoxelMaterialDataPointer; + +/// Contains a block of voxel material data. +class VoxelMaterialData : public DataBlock { +public: + + VoxelMaterialData(const QByteArray& contents); + VoxelMaterialData(Bitstream& in, int bytes); + VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference); const QByteArray& getContents() const { return _contents; } void write(Bitstream& out); - void writeDelta(Bitstream& out, const VoxelSignDataPointer& reference); + void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference); private: diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 2109fea369..8c11196ff6 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -497,31 +497,31 @@ void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjec data.guide(visitor); } -PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius, - const SharedObjectPointer& texture, const QColor& averageColor) : +PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& averageColor) : position(position), radius(radius), - texture(texture), + material(material), averageColor(averageColor) { } -class PaintHeightfieldTextureEditVisitor : public MetavoxelVisitor { +class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor { public: - PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit); + PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit); virtual int visit(MetavoxelInfo& info); private: - PaintHeightfieldTextureEdit _edit; + PaintHeightfieldMaterialEdit _edit; Box _bounds; }; -PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << +PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()), _edit(edit) { @@ -539,28 +539,28 @@ static QHash countIndices(const QByteArray& contents) { return counts; } -int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { +int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; } if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldTextureDataPointer pointer = info.inputValues.at(0).getInlineValue(); + HeightfieldMaterialDataPointer pointer = info.inputValues.at(0).getInlineValue(); if (!pointer) { return STOP_RECURSION; } - QVector textures = pointer->getTextures(); + QVector materials = pointer->getMaterials(); QByteArray contents(pointer->getContents()); - uchar textureIndex = 0; - if (_edit.texture && static_cast(_edit.texture.data())->getURL().isValid()) { - // first look for a matching existing texture, noting the first reusable slot + uchar materialIndex = 0; + if (_edit.material && static_cast(_edit.material.data())->getDiffuse().isValid()) { + // first look for a matching existing material, noting the first reusable slot int firstEmptyIndex = -1; - for (int i = 0; i < textures.size(); i++) { - const SharedObjectPointer& texture = textures.at(i); - if (texture) { - if (texture->equals(_edit.texture.data())) { - textureIndex = i + 1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& material = materials.at(i); + if (material) { + if (material->equals(_edit.material.data())) { + materialIndex = i + 1; break; } } else if (firstEmptyIndex == -1) { @@ -568,26 +568,26 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { } } // if nothing found, use the first empty slot or append - if (textureIndex == 0) { + if (materialIndex == 0) { if (firstEmptyIndex != -1) { - textures[firstEmptyIndex] = _edit.texture; - textureIndex = firstEmptyIndex + 1; + materials[firstEmptyIndex] = _edit.material; + materialIndex = firstEmptyIndex + 1; - } else if (textures.size() < EIGHT_BIT_MAXIMUM) { - textures.append(_edit.texture); - textureIndex = textures.size(); + } else if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(_edit.material); + materialIndex = materials.size(); } else { - // last resort: find the least-used texture and remove it + // last resort: find the least-used material and remove it QHash counts = countIndices(contents); int lowestCount = INT_MAX; for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { if (it.value() < lowestCount) { - textureIndex = it.key(); + materialIndex = it.key(); lowestCount = it.value(); } } - contents.replace((char)textureIndex, (char)0); + contents.replace((char)materialIndex, (char)0); } } } @@ -614,31 +614,31 @@ int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) { for (float x = startX; x <= endX; x += 1.0f, dest++) { float dx = x - center.x, dz = z - center.z; if (dx * dx + dz * dz <= squaredRadius) { - *dest = textureIndex; + *dest = materialIndex; changed = true; } } lineDest += size; } if (changed) { - // clear any unused textures + // clear any unused materials QHash counts = countIndices(contents); - for (int i = 0; i < textures.size(); i++) { + for (int i = 0; i < materials.size(); i++) { if (counts.value(i + 1) == 0) { - textures[i] = SharedObjectPointer(); + materials[i] = SharedObjectPointer(); } } - while (!(textures.isEmpty() || textures.last())) { - textures.removeLast(); + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); } - HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents, textures)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor); return STOP_RECURSION; } -void PaintHeightfieldTextureEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldTextureEditVisitor visitor(*this); +void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + PaintHeightfieldMaterialEditVisitor visitor(*this); data.guide(visitor); } diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 3d610b10df..e4aa711bb5 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -241,23 +241,23 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit) -/// An edit that sets a region of a heightfield texture. -class PaintHeightfieldTextureEdit : public MetavoxelEdit { +/// An edit that sets a region of a heightfield material. +class PaintHeightfieldMaterialEdit : public MetavoxelEdit { STREAMABLE public: STREAM glm::vec3 position; STREAM float radius; - STREAM SharedObjectPointer texture; + STREAM SharedObjectPointer material; STREAM QColor averageColor; - PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, - const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor()); + PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, + const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; }; -DECLARE_STREAMABLE_METATYPE(PaintHeightfieldTextureEdit) +DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit) #endif // hifi_MetavoxelMessages_h From e38d8f7afd7a372967da765e7329d2470d171d03 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 26 Aug 2014 07:47:22 -0700 Subject: [PATCH 04/51] Update all locations that update settings to call bumpSettings() --- interface/src/Application.cpp | 1 + interface/src/Menu.cpp | 16 +++++++++++++- interface/src/Menu.h | 29 +++++++++++++------------- interface/src/ui/PreferencesDialog.cpp | 3 ++- interface/src/ui/RearMirrorTools.cpp | 2 ++ 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8421ee05b1..323dd85822 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3931,6 +3931,7 @@ void Application::setPreviousScriptLocation(const QString& previousScriptLocatio _previousScriptLocation = previousScriptLocation; QMutexLocker locker(&_settingsMutex); _settings->setValue("LastScriptLocation", _previousScriptLocation); + bumpSettings(); } void Application::loadDialog() { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2e2c10458d..a331d9b595 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -718,6 +718,9 @@ void Menu::loadSettings(QSettings* settings) { Application::getInstance()->updateWindowTitle(); NodeList::getInstance()->loadData(settings); + // notify that a settings has changed + connect(&NodeList::getInstance()->getDomainHandler(), &DomainHandler::hostnameChanged, this, &Menu::bumpSettings); + // MyAvatar caches some menu options, so we have to update them whenever we load settings. // TODO: cache more settings in MyAvatar that are checked with very high frequency. MyAvatar* myAvatar = Application::getInstance()->getAvatar(); @@ -880,6 +883,8 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) { default: break; } + + bumpSettings(); } void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) { @@ -986,7 +991,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, QAction::NoRole, menuItemLocation); action->setCheckable(true); action->setChecked(checked); - connect(action, SIGNAL(changed()), Application::getInstance(), SLOT(bumpSettings())); + connect(action, SIGNAL(changed()), this, SLOT(bumpSettings())); return action; } @@ -1028,6 +1033,10 @@ void Menu::aboutApp() { InfoView::forcedShow(); } +void Menu::bumpSettings() { + Application::getInstance()->bumpSettings(); +} + void sendFakeEnterEvent() { QPoint lastCursorPosition = QCursor::pos(); QGLWidget* glWidget = Application::getInstance()->getGLWidget(); @@ -1089,6 +1098,8 @@ void Menu::changePrivateKey() { // pull the private key from the dialog _walletPrivateKey = privateKeyDialog.textValue().toUtf8(); } + + bumpSettings(); sendFakeEnterEvent(); } @@ -1611,10 +1622,12 @@ void Menu::resetLODAdjust() { void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; + bumpSettings(); } void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; + bumpSettings(); } void Menu::lodTools() { @@ -1904,5 +1917,6 @@ QString Menu::getSnapshotsLocation() const { void Menu::setScriptsLocation(const QString& scriptsLocation) { _scriptsLocation = scriptsLocation; + bumpSettings(); emit scriptLocationChanged(scriptsLocation); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index efc812fb22..4a6551a6f3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -90,24 +90,24 @@ public: QAction* getActionForOption(const QString& menuOption); float getAudioJitterBufferFrames() const { return _audioJitterBufferFrames; } - void setAudioJitterBufferFrames(float audioJitterBufferSamples) { _audioJitterBufferFrames = audioJitterBufferSamples; } + void setAudioJitterBufferFrames(float audioJitterBufferSamples) { _audioJitterBufferFrames = audioJitterBufferSamples; bumpSettings(); } int getMaxFramesOverDesired() const { return _maxFramesOverDesired; } - void setMaxFramesOverDesired(int maxFramesOverDesired) { _maxFramesOverDesired = maxFramesOverDesired; } + void setMaxFramesOverDesired(int maxFramesOverDesired) { _maxFramesOverDesired = maxFramesOverDesired; bumpSettings(); } float getFieldOfView() const { return _fieldOfView; } - void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; } + void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; bumpSettings(); } float getRealWorldFieldOfView() const { return _realWorldFieldOfView; } - void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; } + void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; bumpSettings(); } float getOculusUIAngularSize() const { return _oculusUIAngularSize; } - void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } + void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; bumpSettings(); } float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; } - void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; } + void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; bumpSettings(); } bool getInvertSixenseButtons() const { return _invertSixenseButtons; } - void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; } + void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; bumpSettings(); } float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; } - void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; } + void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; bumpSettings(); } QString getSnapshotsLocation() const; - void setSnapshotsLocation(QString snapshotsLocation) { _snapshotsLocation = snapshotsLocation; } + void setSnapshotsLocation(QString snapshotsLocation) { _snapshotsLocation = snapshotsLocation; bumpSettings(); } const QString& getScriptsLocation() const { return _scriptsLocation; } void setScriptsLocation(const QString& scriptsLocation); @@ -130,13 +130,13 @@ public: void resetLODAdjust(); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } - void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; } + void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; bumpSettings(); } bool getAutomaticAvatarLOD() const { return _automaticAvatarLOD; } - void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; } + void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; bumpSettings(); } float getAvatarLODDecreaseFPS() const { return _avatarLODDecreaseFPS; } - void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; } + void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; bumpSettings(); } float getAvatarLODIncreaseFPS() const { return _avatarLODIncreaseFPS; } - void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; } + void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; bumpSettings(); } float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } void setBoundaryLevelAdjust(int boundaryLevelAdjust); int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } @@ -147,7 +147,7 @@ public: // User Tweakable PPS from Voxel Server int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; } - void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; } + void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; bumpSettings(); } QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString& actionName, @@ -206,6 +206,7 @@ public slots: private slots: void aboutApp(); + void bumpSettings(); void editPreferences(); void editAttachments(); void editAnimations(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 4ebd5f4c1a..1713209e87 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -203,7 +203,6 @@ void PreferencesDialog::savePreferences() { if (shouldDispatchIdentityPacket) { myAvatar->sendIdentityPacket(); - Application::getInstance()->bumpSettings(); } if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger) @@ -254,4 +253,6 @@ void PreferencesDialog::savePreferences() { Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height()); + + Application::getInstance()->bumpSettings(); } diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp index 9620469a22..f4b0bb82b5 100644 --- a/interface/src/ui/RearMirrorTools.cpp +++ b/interface/src/ui/RearMirrorTools.cpp @@ -88,11 +88,13 @@ bool RearMirrorTools::mousePressEvent(int x, int y) { if (_headZoomIconRect.contains(x, y)) { _zoomLevel = HEAD; + Application::getInstance()->bumpSettings(); return true; } if (_bodyZoomIconRect.contains(x, y)) { _zoomLevel = BODY; + Application::getInstance()->bumpSettings(); return true; } From 337fe42390fbbfa4358448dacc93302277afd6de Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 26 Aug 2014 11:34:46 -0700 Subject: [PATCH 05/51] Method renaming. --- libraries/metavoxels/src/AttributeRegistry.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 6d7d27c934..b9a5e9218b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -926,10 +926,10 @@ void HeightfieldColorData::set(const QImage& image) { memcpy(_contents.data(), image.constBits(), _contents.size()); } -const int MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; +const int HEIGHTFIELD_MATERIAL_HEADER_SIZE = sizeof(qint32) * 4; -static QByteArray encodeMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(MATERIAL_HEADER_SIZE, 0); +static QByteArray encodeHeightfieldMaterial(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { + QByteArray inflated(HEIGHTFIELD_MATERIAL_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; @@ -939,14 +939,14 @@ static QByteArray encodeMaterial(int offsetX, int offsetY, int width, int height return qCompress(inflated); } -static QByteArray decodeMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { +static QByteArray decodeHeightfieldMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) { QByteArray inflated = qUncompress(encoded); const qint32* header = (const qint32*)inflated.constData(); offsetX = *header++; offsetY = *header++; width = *header++; height = *header++; - return inflated.mid(MATERIAL_HEADER_SIZE); + return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); } HeightfieldMaterialData::HeightfieldMaterialData(const QByteArray& contents, const QVector& materials) : @@ -970,7 +970,7 @@ HeightfieldMaterialData::HeightfieldMaterialData(Bitstream& in, int bytes, const _contents = reference->getContents(); int offsetX, offsetY, width, height; - QByteArray delta = decodeMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); + QByteArray delta = decodeHeightfieldMaterial(reference->getEncodedDelta(), offsetX, offsetY, width, height); if (delta.isEmpty()) { return; } @@ -992,7 +992,7 @@ void HeightfieldMaterialData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { int size = glm::sqrt((float)_contents.size()); - _encoded = encodeMaterial(0, 0, size, size, _contents); + _encoded = encodeHeightfieldMaterial(0, 0, size, size, _contents); } out << _encoded.size(); out.writeAligned(_encoded); @@ -1037,7 +1037,7 @@ void HeightfieldMaterialData::writeDelta(Bitstream& out, const HeightfieldMateri memcpy(dest, src, width); } } - reference->setEncodedDelta(encodeMaterial(minX + 1, minY + 1, width, height, delta)); + reference->setEncodedDelta(encodeHeightfieldMaterial(minX + 1, minY + 1, width, height, delta)); reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); @@ -1047,7 +1047,7 @@ void HeightfieldMaterialData::writeDelta(Bitstream& out, const HeightfieldMateri void HeightfieldMaterialData::read(Bitstream& in, int bytes) { int offsetX, offsetY, width, height; - _contents = decodeMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); + _contents = decodeHeightfieldMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height); in >> _materials; } From c6486b7f237db5d0442f47fde62f7697bb20648c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 26 Aug 2014 14:43:54 -0700 Subject: [PATCH 06/51] Voxel material bits. --- .../metavoxels/src/AttributeRegistry.cpp | 189 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 30 ++- 2 files changed, 211 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b9a5e9218b..985237f412 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -52,7 +52,8 @@ AttributeRegistry::AttributeRegistry() : _spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))), _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), - _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))) { + _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), + _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))) { // our baseline LOD threshold is for voxels; spanners and heightfields are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -62,6 +63,9 @@ AttributeRegistry::AttributeRegistry() : _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); + + const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 32.0f; + _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -1431,8 +1435,39 @@ void VoxelColorData::read(Bitstream& in, int bytes) { } -VoxelMaterialData::VoxelMaterialData(const QByteArray& contents) : - _contents(contents) { +const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6; + +static QByteArray encodeVoxelMaterial(int offsetX, int offsetY, int offsetZ, + int sizeX, int sizeY, int sizeZ, const QByteArray& contents) { + QByteArray inflated(VOXEL_MATERIAL_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = offsetZ; + *header++ = sizeX; + *header++ = sizeY; + *header++ = sizeZ; + inflated.append(contents); + return qCompress(inflated); +} + +static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ, + int& sizeX, int& sizeY, int& sizeZ) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + offsetZ = *header++; + sizeX = *header++; + sizeY = *header++; + sizeZ = *header++; + return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); +} + +VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector& materials) : + _contents(contents), + _size(size), + _materials(materials) { } VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) { @@ -1446,34 +1481,174 @@ VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMateri } QMutexLocker locker(&reference->getEncodedDeltaMutex()); reference->setEncodedDelta(in.readAligned(bytes)); + in.readDelta(_materials, reference->getMaterials()); reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); + _size = reference->getSize(); + + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + QByteArray delta = decodeVoxelMaterial(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _size = sizeX; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + int minZ = offsetZ - 1; + const char* src = delta.constData(); + int size2 = _size * _size; + char* planeDest = _contents.data() + minZ * size2 + minY * _size + minX; + for (int z = 0; z < sizeZ; z++, planeDest += size2) { + char* dest = planeDest; + for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) { + memcpy(dest, src, sizeX); + } + } } void VoxelMaterialData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - + _encoded = encodeVoxelMaterial(0, 0, 0, _size, _size, _size, _contents); } out << _encoded.size(); out.writeAligned(_encoded); + out << _materials; } void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { + if (!reference || reference->getSize() != _size) { write(out); return; } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - + int minX = _size, minY = _size, minZ = _size; + int maxX = -1, maxY = -1, maxZ = -1; + const char* src = _contents.constData(); + const char* ref = reference->getContents().constData(); + for (int z = 0; z < _size; z++) { + bool differenceZ = false; + for (int y = 0; y < _size; y++) { + bool differenceY = false; + for (int x = 0; x < _size; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + differenceY = differenceZ = true; + } + } + if (differenceY) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + if (differenceZ) { + minZ = qMin(minZ, z); + maxZ = qMax(maxZ, z); + } + } + QByteArray delta; + int sizeX = 0, sizeY = 0, sizeZ = 0; + if (maxX >= minX) { + sizeX = maxX - minX + 1; + sizeY = maxY - minY + 1; + sizeZ = maxZ - minZ + 1; + delta = QByteArray(sizeX * sizeY * sizeZ, 0); + char* dest = delta.data(); + int size2 = _size * _size; + const char* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX; + for (int z = 0; z < sizeZ; z++, planeSrc += size2) { + src = planeSrc; + for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) { + memcpy(dest, src, sizeX); + } + } + } + reference->setEncodedDelta(encodeVoxelMaterial(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); + out.writeDelta(_materials, reference->getMaterials()); } void VoxelMaterialData::read(Bitstream& in, int bytes) { - + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + _contents = decodeVoxelMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + _size = sizeX; + in >> _materials; +} + +VoxelMaterialAttribute::VoxelMaterialAttribute(const QString& name) : + InlineAttribute(name) { +} + +void VoxelMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(); + } else { + *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(in, size)); + } +} + +void VoxelMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelMaterialDataPointer data = decodeInline(value); + if (data) { + data->write(out); + } else { + out << 0; + } +} + +void VoxelMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(); + } else { + *(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData( + in, size, decodeInline(reference))); + } +} + +void VoxelMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelMaterialDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference)); + } else { + out << 0; + } +} + +bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const { + int maxSize = 0; + for (int i = 0; i < MERGE_COUNT; i++) { + VoxelMaterialDataPointer pointer = decodeInline(children[i]); + if (pointer) { + maxSize = qMax(maxSize, pointer->getContents().size()); + } + } + *(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer(); + return maxSize == 0; } SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index d7b6575a63..10156af1d9 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -111,6 +111,9 @@ public: /// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute. const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; } + /// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute. + const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -129,6 +132,7 @@ private: AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; AttributePointer _heightfieldMaterialAttribute; + AttributePointer _voxelMaterialAttribute; }; /// Converts a value to a void pointer. @@ -655,12 +659,17 @@ typedef QExplicitlySharedDataPointer VoxelMaterialDataPointer class VoxelMaterialData : public DataBlock { public: - VoxelMaterialData(const QByteArray& contents); + VoxelMaterialData(const QByteArray& contents, int size, + const QVector& materials = QVector()); VoxelMaterialData(Bitstream& in, int bytes); VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference); const QByteArray& getContents() const { return _contents; } + int getSize() const { return _size; } + + const QVector& getMaterials() const { return _materials; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference); @@ -669,6 +678,25 @@ private: void read(Bitstream& in, int bytes); QByteArray _contents; + int _size; + QVector _materials; +}; + +/// An attribute that stores voxel materials. +class VoxelMaterialAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE VoxelMaterialAttribute(const QString& name = QString()); + + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). From 9c0888afff6c3e814ca0e32db1bd1339ca157a02 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 26 Aug 2014 17:15:02 -0700 Subject: [PATCH 07/51] Working on voxel edits. --- interface/src/ui/MetavoxelEditor.cpp | 148 ++++++++++--- interface/src/ui/MetavoxelEditor.h | 85 +++++++- .../metavoxels/src/AttributeRegistry.cpp | 196 +++++++++++++++++- libraries/metavoxels/src/AttributeRegistry.h | 31 ++- .../metavoxels/src/MetavoxelMessages.cpp | 20 ++ libraries/metavoxels/src/MetavoxelMessages.h | 36 ++++ 6 files changed, 466 insertions(+), 50 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 40e1b75970..55609b8e16 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -66,13 +66,16 @@ MetavoxelEditor::MetavoxelEditor() : attributeLayout->addLayout(attributeButtonLayout); QPushButton* newAttribute = new QPushButton("New..."); - attributeButtonLayout->addWidget(newAttribute); + attributeButtonLayout->addWidget(newAttribute, 1); connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); - attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete")); + attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1); _deleteAttribute->setEnabled(false); connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute())); + attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All")); + connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes())); + QFormLayout* formLayout = new QFormLayout(); topLayout->addLayout(formLayout); @@ -116,11 +119,13 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new RemoveSpannerTool(this)); addTool(new ClearSpannersTool(this)); addTool(new SetSpannerTool(this)); - addTool(new ImportHeightfieldTool(this)); - addTool(new EraseHeightfieldTool(this)); addTool(new HeightfieldHeightBrushTool(this)); addTool(new HeightfieldColorBrushTool(this)); addTool(new HeightfieldMaterialBrushTool(this)); + addTool(new ImportHeightfieldTool(this)); + addTool(new EraseHeightfieldTool(this)); + addTool(new VoxelColorBoxTool(this)); + addTool(new VoxelMaterialBoxTool(this)); updateAttributes(); @@ -200,7 +205,7 @@ void MetavoxelEditor::selectedAttributeChanged() { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); foreach (MetavoxelTool* tool, _tools) { - if (tool->appliesTo(attribute)) { + if (tool->appliesTo(attribute) && (tool->isUserFacing() || _showAll->isChecked())) { _toolBox->addItem(tool->objectName(), QVariant::fromValue(tool)); } } @@ -271,6 +276,35 @@ void MetavoxelEditor::alignGridPosition() { _gridPosition->setValue(step * floor(_gridPosition->value() / step)); } +void MetavoxelEditor::updateAttributes(const QString& select) { + // remember the selection in order to preserve it + QString selected = select.isNull() ? getSelectedAttribute() : select; + _attributes->clear(); + + // sort the names for consistent ordering + QList names; + if (_showAll->isChecked()) { + names = AttributeRegistry::getInstance()->getAttributes().keys(); + + } else { + foreach (const AttributePointer& attribute, AttributeRegistry::getInstance()->getAttributes()) { + if (attribute->isUserFacing()) { + names.append(attribute->getName()); + } + } + } + qSort(names); + + foreach (const QString& name, names) { + QListWidgetItem* item = new QListWidgetItem(name); + _attributes->addItem(item); + if (name == selected || selected.isNull()) { + item->setSelected(true); + selected = name; + } + } +} + void MetavoxelEditor::updateTool() { MetavoxelTool* active = getActiveTool(); foreach (MetavoxelTool* tool, _tools) { @@ -335,25 +369,6 @@ void MetavoxelEditor::addTool(MetavoxelTool* tool) { layout()->addWidget(tool); } -void MetavoxelEditor::updateAttributes(const QString& select) { - // remember the selection in order to preserve it - QString selected = select.isNull() ? getSelectedAttribute() : select; - _attributes->clear(); - - // sort the names for consistent ordering - QList names = AttributeRegistry::getInstance()->getAttributes().keys(); - qSort(names); - - foreach (const QString& name, names) { - QListWidgetItem* item = new QListWidgetItem(name); - _attributes->addItem(item); - if (name == selected || selected.isNull()) { - item->setSelected(true); - selected = name; - } - } -} - MetavoxelTool* MetavoxelEditor::getActiveTool() const { int index = _toolBox->currentIndex(); return (index == -1) ? NULL : static_cast(_toolBox->itemData(index).value()); @@ -361,9 +376,10 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const { ProgramObject MetavoxelEditor::_gridProgram; -MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue) : +MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : _editor(editor), - _usesValue(usesValue) { + _usesValue(usesValue), + _userFacing(userFacing) { QVBoxLayout* layout = new QVBoxLayout(); setLayout(layout); @@ -385,13 +401,13 @@ void MetavoxelTool::render() { // nothing by default } -BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Set Value (Box)") { +BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) : + MetavoxelTool(editor, name, usesValue, userFacing) { resetState(); } -void BoxSetTool::render() { +void BoxTool::render() { if (Application::getInstance()->isMouseHidden()) { resetState(); return; @@ -457,7 +473,7 @@ void BoxSetTool::render() { glTranslatef(0.5f, 0.5f, 0.5f); if (_state != HOVERING_STATE) { const float BOX_ALPHA = 0.25f; - QColor color = _editor->getValue().value(); + QColor color = getColor(); if (color.isValid()) { glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); } else { @@ -476,7 +492,7 @@ void BoxSetTool::render() { glPopMatrix(); } -bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) { +bool BoxTool::eventFilter(QObject* watched, QEvent* event) { switch (_state) { case HOVERING_STATE: if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { @@ -515,12 +531,20 @@ bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) { return false; } -void BoxSetTool::resetState() { +void BoxTool::resetState() { _state = HOVERING_STATE; _startPosition = INVALID_VECTOR; _height = 0.0f; } +BoxSetTool::BoxSetTool(MetavoxelEditor* editor) : + BoxTool(editor, "Set Value (Box)", true, false) { +} + +QColor BoxSetTool::getColor() { + return _editor->getValue().value(); +} + void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute()); if (!attribute) { @@ -533,7 +557,7 @@ void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) } GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) : - MetavoxelTool(editor, "Set Value (Global)") { + MetavoxelTool(editor, "Set Value (Global)", true, false) { QPushButton* button = new QPushButton("Apply"); layout()->addWidget(button); @@ -1199,3 +1223,61 @@ void HeightfieldMaterialBrushTool::updateTexture() { MaterialObject* material = static_cast(_materialEditor->getObject().data()); _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); } + +VoxelColorBoxTool::VoxelColorBoxTool(MetavoxelEditor* editor) : + BoxTool(editor, "Set Voxel Color (Box)", false) { + + QWidget* widget = new QWidget(); + QFormLayout* form = new QFormLayout(); + widget->setLayout(form); + layout()->addWidget(widget); + + form->addRow("Color:", _color = new QColorEditor(this)); +} + +bool VoxelColorBoxTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("VoxelColorAttribute"); +} + +QColor VoxelColorBoxTool::getColor() { + return _color->getColor(); +} + +void VoxelColorBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorBoxEdit(Box(minimum, maximum), + _editor->getGridSpacing(), _color->getColor())) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); +} + +VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) : + BoxTool(editor, "Set Voxel Material (Box)", false) { + + QWidget* widget = new QWidget(); + QFormLayout* form = new QFormLayout(); + widget->setLayout(form); + layout()->addWidget(widget); + + form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); + connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBoxTool::updateTexture); +} + +bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("VoxelColorAttribute"); +} + +QColor VoxelMaterialBoxTool::getColor() { + return _texture ? _texture->getAverageColor() : QColor(); +} + +void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + SharedObjectPointer material = _materialEditor->getObject(); + _materialEditor->detachObject(); + MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialBoxEdit(Box(minimum, maximum), + _editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); +} + +void VoxelMaterialBoxTool::updateTexture() { + MaterialObject* material = static_cast(_materialEditor->getObject().data()); + _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index a739f258cc..ba455dd96c 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -57,6 +57,7 @@ private slots: void deleteSelectedAttribute(); void centerGridPosition(); void alignGridPosition(); + void updateAttributes(const QString& select = QString()); void updateTool(); void simulate(float deltaTime); @@ -65,11 +66,11 @@ private slots: private: void addTool(MetavoxelTool* tool); - void updateAttributes(const QString& select = QString()); MetavoxelTool* getActiveTool() const; QListWidget* _attributes; QPushButton* _deleteAttribute; + QCheckBox* _showAll; QComboBox* _gridPlane; QDoubleSpinBox* _gridSpacing; @@ -90,10 +91,12 @@ class MetavoxelTool : public QWidget { public: - MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true); + MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true); bool getUsesValue() const { return _usesValue; } + bool isUserFacing() const { return _userFacing; } + virtual bool appliesTo(const AttributePointer& attribute) const; virtual void simulate(float deltaTime); @@ -105,24 +108,30 @@ protected: MetavoxelEditor* _editor; bool _usesValue; + bool _userFacing; }; -/// Allows setting the value of a region by dragging out a box. -class BoxSetTool : public MetavoxelTool { +/// Base class for tools that allow dragging out a 3D box. +class BoxTool : public MetavoxelTool { Q_OBJECT public: - BoxSetTool(MetavoxelEditor* editor); - + BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true); + virtual void render(); virtual bool eventFilter(QObject* watched, QEvent* event); +protected: + + virtual QColor getColor() = 0; + + virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum) = 0; + private: void resetState(); - void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; @@ -134,6 +143,21 @@ private: float _height; ///< the selection height }; +/// Allows setting the value of a region by dragging out a box. +class BoxSetTool : public BoxTool { + Q_OBJECT + +public: + + BoxSetTool(MetavoxelEditor* editor); + +protected: + + virtual QColor getColor(); + + virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); +}; + /// Allows setting the value across the entire space. class GlobalSetTool : public MetavoxelTool { Q_OBJECT @@ -384,4 +408,51 @@ private: QSharedPointer _texture; }; +/// Allows setting voxel colors by dragging out a box. +class VoxelColorBoxTool : public BoxTool { + Q_OBJECT + +public: + + VoxelColorBoxTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QColor getColor(); + + virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + +private: + + QColorEditor* _color; +}; + +/// Allows setting voxel materials by dragging out a box. +class VoxelMaterialBoxTool : public BoxTool { + Q_OBJECT + +public: + + VoxelMaterialBoxTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QColor getColor(); + + virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + +private slots: + + void updateTexture(); + +private: + + SharedObjectEditor* _materialEditor; + QSharedPointer _texture; +}; + #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 985237f412..c8af7c05a8 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -30,6 +30,8 @@ REGISTER_META_OBJECT(HeightfieldMaterialAttribute) REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) +REGISTER_META_OBJECT(VoxelColorAttribute) +REGISTER_META_OBJECT(VoxelMaterialAttribute) static int attributePointerMetaTypeId = qRegisterMetaType(); static int ownedAttributeValueMetaTypeId = qRegisterMetaType(); @@ -53,6 +55,7 @@ AttributeRegistry::AttributeRegistry() : _heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))), _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), + _voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))), _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))) { // our baseline LOD threshold is for voxels; spanners and heightfields are a different story @@ -61,10 +64,13 @@ AttributeRegistry::AttributeRegistry() : const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f; _heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); + _heightfieldAttribute->setUserFacing(true); _heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); _heightfieldMaterialAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER); const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 32.0f; + _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); + _voxelColorAttribute->setUserFacing(true); _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); } @@ -205,7 +211,8 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& o } Attribute::Attribute(const QString& name) : - _lodThresholdMultiplier(1.0f) { + _lodThresholdMultiplier(1.0f), + _userFacing(false) { setObjectName(name); } @@ -1390,8 +1397,41 @@ bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool p return maxSize == 0; } -VoxelColorData::VoxelColorData(const QVector& contents) : - _contents(contents) { +const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; + +static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, + int sizeX, int sizeY, int sizeZ, const QVector& contents) { + QByteArray inflated(VOXEL_COLOR_HEADER_SIZE, 0); + qint32* header = (qint32*)inflated.data(); + *header++ = offsetX; + *header++ = offsetY; + *header++ = offsetZ; + *header++ = sizeX; + *header++ = sizeY; + *header++ = sizeZ; + inflated.append((const char*)contents.constData(), contents.size() * sizeof(QRgb)); + return qCompress(inflated); +} + +static QVector decodeVoxelColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ, + int& sizeX, int& sizeY, int& sizeZ) { + QByteArray inflated = qUncompress(encoded); + const qint32* header = (const qint32*)inflated.constData(); + offsetX = *header++; + offsetY = *header++; + offsetZ = *header++; + sizeX = *header++; + sizeY = *header++; + sizeZ = *header++; + int payloadSize = inflated.size() - VOXEL_COLOR_HEADER_SIZE; + QVector contents(payloadSize / sizeof(QRgb)); + memcpy(contents.data(), inflated.constData() + VOXEL_COLOR_HEADER_SIZE, payloadSize); + return contents; +} + +VoxelColorData::VoxelColorData(const QVector& contents, int size) : + _contents(contents), + _size(size) { } VoxelColorData::VoxelColorData(Bitstream& in, int bytes) { @@ -1407,32 +1447,170 @@ VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPoi reference->setEncodedDelta(in.readAligned(bytes)); reference->setDeltaData(DataBlockPointer(this)); _contents = reference->getContents(); + _size = reference->getSize(); + + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + QVector delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _size = sizeX; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + int minZ = offsetZ - 1; + const QRgb* src = delta.constData(); + int size2 = _size * _size; + QRgb* planeDest = _contents.data() + minZ * size2 + minY * _size + minX; + int length = sizeX * sizeof(QRgb); + for (int z = 0; z < sizeZ; z++, planeDest += size2) { + QRgb* dest = planeDest; + for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) { + memcpy(dest, src, length); + } + } } void VoxelColorData::write(Bitstream& out) { QMutexLocker locker(&_encodedMutex); if (_encoded.isEmpty()) { - + _encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents); } out << _encoded.size(); out.writeAligned(_encoded); } void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) { - if (!reference || reference->getContents().size() != _contents.size()) { + if (!reference || reference->getSize() != _size) { write(out); return; } QMutexLocker locker(&reference->getEncodedDeltaMutex()); if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { - + int minX = _size, minY = _size, minZ = _size; + int maxX = -1, maxY = -1, maxZ = -1; + const QRgb* src = _contents.constData(); + const QRgb* ref = reference->getContents().constData(); + for (int z = 0; z < _size; z++) { + bool differenceZ = false; + for (int y = 0; y < _size; y++) { + bool differenceY = false; + for (int x = 0; x < _size; x++) { + if (*src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + differenceY = differenceZ = true; + } + } + if (differenceY) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + if (differenceZ) { + minZ = qMin(minZ, z); + maxZ = qMax(maxZ, z); + } + } + QVector delta; + int sizeX = 0, sizeY = 0, sizeZ = 0; + if (maxX >= minX) { + sizeX = maxX - minX + 1; + sizeY = maxY - minY + 1; + sizeZ = maxZ - minZ + 1; + delta = QVector(sizeX * sizeY * sizeZ, 0); + QRgb* dest = delta.data(); + int size2 = _size * _size; + const QRgb* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX; + int length = sizeX * sizeof(QRgb); + for (int z = 0; z < sizeZ; z++, planeSrc += size2) { + src = planeSrc; + for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) { + memcpy(dest, src, length); + } + } + } + reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); + reference->setDeltaData(DataBlockPointer(this)); } out << reference->getEncodedDelta().size(); out.writeAligned(reference->getEncodedDelta()); } void VoxelColorData::read(Bitstream& in, int bytes) { - + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + _contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + _size = sizeX; +} + +VoxelColorAttribute::VoxelColorAttribute(const QString& name) : + InlineAttribute(name) { +} + +void VoxelColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(); + } else { + *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(in, size)); + } +} + +void VoxelColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelColorDataPointer data = decodeInline(value); + if (data) { + data->write(out); + } else { + out << 0; + } +} + +void VoxelColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(); + } else { + *(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData( + in, size, decodeInline(reference))); + } +} + +void VoxelColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelColorDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference)); + } else { + out << 0; + } +} + +bool VoxelColorAttribute::merge(void*& parent, void* children[], bool postRead) const { + int maxSize = 0; + for (int i = 0; i < MERGE_COUNT; i++) { + VoxelColorDataPointer pointer = decodeInline(children[i]); + if (pointer) { + maxSize = qMax(maxSize, pointer->getSize()); + } + } + *(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(); + return maxSize == 0; } const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6; @@ -1461,7 +1639,7 @@ static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, i sizeX = *header++; sizeY = *header++; sizeZ = *header++; - return inflated.mid(HEIGHTFIELD_MATERIAL_HEADER_SIZE); + return inflated.mid(VOXEL_MATERIAL_HEADER_SIZE); } VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector& materials) : @@ -1644,7 +1822,7 @@ bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRea for (int i = 0; i < MERGE_COUNT; i++) { VoxelMaterialDataPointer pointer = decodeInline(children[i]); if (pointer) { - maxSize = qMax(maxSize, pointer->getContents().size()); + maxSize = qMax(maxSize, pointer->getSize()); } } *(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer(); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 10156af1d9..b12fcbe6a1 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -111,6 +111,9 @@ public: /// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute. const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; } + /// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute. + const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; } + /// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute. const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; } @@ -132,6 +135,7 @@ private: AttributePointer _heightfieldAttribute; AttributePointer _heightfieldColorAttribute; AttributePointer _heightfieldMaterialAttribute; + AttributePointer _voxelColorAttribute; AttributePointer _voxelMaterialAttribute; }; @@ -212,6 +216,7 @@ Q_DECLARE_METATYPE(OwnedAttributeValue) class Attribute : public SharedObject { Q_OBJECT Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier) + Q_PROPERTY(bool userFacing MEMBER _userFacing) public: @@ -225,6 +230,9 @@ public: float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; } void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; } + bool isUserFacing() const { return _userFacing; } + void setUserFacing(bool userFacing) { _userFacing = userFacing; } + void* create() const { return create(getDefaultValue()); } virtual void* create(void* copy) const = 0; virtual void destroy(void* value) const = 0; @@ -289,6 +297,7 @@ public: private: float _lodThresholdMultiplier; + bool _userFacing; }; /// A simple attribute class that stores its values inline. @@ -637,12 +646,14 @@ typedef QExplicitlySharedDataPointer VoxelColorDataPointer; class VoxelColorData : public DataBlock { public: - VoxelColorData(const QVector& contents); + VoxelColorData(const QVector& contents, int size); VoxelColorData(Bitstream& in, int bytes); VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference); const QVector& getContents() const { return _contents; } + int getSize() const { return _size; } + void write(Bitstream& out); void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference); @@ -651,6 +662,24 @@ private: void read(Bitstream& in, int bytes); QVector _contents; + int _size; +}; + +/// An attribute that stores voxel colors. +class VoxelColorAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE VoxelColorAttribute(const QString& name = QString()); + + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; typedef QExplicitlySharedDataPointer VoxelMaterialDataPointer; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 8c11196ff6..88a3fb9ab1 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -642,3 +642,23 @@ void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedOb PaintHeightfieldMaterialEditVisitor visitor(*this); data.guide(visitor); } + +VoxelColorBoxEdit::VoxelColorBoxEdit(const Box& region, float granularity, const QColor& color) : + region(region), + granularity(granularity), + color(color) { +} + +void VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { +} + +VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity, + const SharedObjectPointer& material, const QColor& averageColor) : + region(region), + granularity(granularity), + material(material), + averageColor(averageColor) { +} + +void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index e4aa711bb5..15ab0d92cb 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -260,4 +260,40 @@ public: DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit) +/// An edit that sets the color of voxels within a box to a value. +class VoxelColorBoxEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM Box region; + STREAM float granularity; + STREAM QColor color; + + VoxelColorBoxEdit(const Box& region = Box(), float granularity = 0.0f, const QColor& color = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(VoxelColorBoxEdit) + +/// An edit that sets the materials of voxels within a box to a value. +class VoxelMaterialBoxEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM Box region; + STREAM float granularity; + STREAM SharedObjectPointer material; + STREAM QColor averageColor; + + VoxelMaterialBoxEdit(const Box& region = Box(), float granularity = 0.0f, + const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(VoxelMaterialBoxEdit) + #endif // hifi_MetavoxelMessages_h From e87556450e6d03a0069746bcb8fc79ed3a64552b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 26 Aug 2014 18:02:55 -0700 Subject: [PATCH 08/51] More voxel edit bits. --- interface/src/ui/MetavoxelEditor.cpp | 1 + .../metavoxels/src/MetavoxelMessages.cpp | 66 ++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 55609b8e16..5702db39a2 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -219,6 +219,7 @@ void MetavoxelEditor::selectedAttributeChanged() { editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); _valueArea->setWidget(editor); } + updateTool(); } void MetavoxelEditor::createNewAttribute() { diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 88a3fb9ab1..8916e98a43 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -649,7 +649,38 @@ VoxelColorBoxEdit::VoxelColorBoxEdit(const Box& region, float granularity, const color(color) { } +class VoxelColorBoxEditVisitor : public MetavoxelVisitor { +public: + + VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit); + + virtual int visit(MetavoxelInfo& info); + +private: + + const VoxelColorBoxEdit& _edit; +}; + +VoxelColorBoxEditVisitor::VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute(), + QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute()), + _edit(edit) { +} + +int VoxelColorBoxEditVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + return STOP_RECURSION; +} + void VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // expand to fit the entire edit + while (!data.getBounds().contains(region)) { + data.expand(); + } + VoxelColorBoxEditVisitor setVisitor(*this); + data.guide(setVisitor); } VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity, @@ -660,5 +691,38 @@ VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity, averageColor(averageColor) { } -void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { +class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor { +public: + + VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit); + + virtual int visit(MetavoxelInfo& info); + +private: + + const VoxelMaterialBoxEdit& _edit; +}; + +VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << + AttributeRegistry::getInstance()->getVoxelColorAttribute(), QVector() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << + AttributeRegistry::getInstance()->getVoxelColorAttribute()), + _edit(edit) { +} + +int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + return STOP_RECURSION; +} + +void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // expand to fit the entire edit + while (!data.getBounds().contains(region)) { + data.expand(); + } + VoxelMaterialBoxEditVisitor setVisitor(*this); + data.guide(setVisitor); } From f1aaea0ffa1a3221b2adc55e8ee78c26a65ac9e9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 27 Aug 2014 12:05:57 -0700 Subject: [PATCH 09/51] Fix bug with chat scrolling randomly --- interface/src/ui/ChatWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 8874f18dba..a04212afe0 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -124,6 +124,10 @@ void ChatWindow::showEvent(QShowEvent* event) { ui->messagePlainTextEdit->setFocus(); } + Application::processEvents(); + + scrollToBottom(); + #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); if (xmppClient.isConnected()) { From 9a4d56b4e9be13ae917e1f0c15dd1997ce275a82 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 27 Aug 2014 14:41:23 -0700 Subject: [PATCH 10/51] Working on voxel bits. --- interface/src/MetavoxelSystem.cpp | 93 ++++++++++++++++++- interface/src/MetavoxelSystem.h | 27 ++++++ .../metavoxels/src/MetavoxelMessages.cpp | 40 +++++++- 3 files changed, 154 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 657b018f27..72030d089a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -34,12 +34,18 @@ static int bufferPointVectorMetaTypeId = qRegisterMetaType(); void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); - _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); - _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( - new BufferDataAttribute("heightfieldBuffer")); + _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer")); + + _heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( + new BufferDataAttribute("heightfieldBuffer")); _heightfieldBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getHeightfieldAttribute()->getLODThresholdMultiplier()); + + _voxelBufferAttribute = AttributeRegistry::getInstance()->registerAttribute( + new BufferDataAttribute("voxelBuffer")); + _voxelBufferAttribute->setLODThresholdMultiplier( + AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -972,6 +978,16 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glEnable(GL_BLEND); } +VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, + const QVector& materials) : + _vertices(vertices), + _indices(indices), + _materials(materials) { +} + +void VoxelBuffer::render(bool cursor) { +} + BufferDataAttribute::BufferDataAttribute(const QString& name) : InlineAttribute(name) { } @@ -1213,6 +1229,8 @@ HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const _intersections(intersections) { } +const int EIGHT_BIT_MAXIMUM = 255; + int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { Box bounds = info.getBounds(); const Box& heightBounds = _buffer->getHeightBounds(); @@ -1263,7 +1281,6 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { shift++; size *= 2.0f; } - const int EIGHT_BIT_MAXIMUM = 255; int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; @@ -1479,6 +1496,71 @@ int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } +class VoxelAugmentVisitor : public MetavoxelVisitor { +public: + + VoxelAugmentVisitor(const MetavoxelLOD& lod); + + virtual int visit(MetavoxelInfo& info); +}; + +VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << + Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { +} + +int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { + if (!info.isLeaf) { + return DEFAULT_ORDER; + } + VoxelBuffer* buffer = NULL; + VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue(); + if (color) { + QVector vertices; + QVector indices; + + const QVector& contents = color->getContents(); + int size = color->getSize(); + int area = size * size; + int highest = size - 1; + int offset3 = size + 1; + int offset5 = area + 1; + int offset6 = area + size; + int offset7 = area + size + 1; + + const QRgb* src = contents.constData(); + + const int ALPHA_OFFSET = 24; + const int MAX_ALPHA_TOTAL = EIGHT_BIT_MAXIMUM * 8; + for (int z = 0; z < highest; z++) { + for (int y = 0; y < highest; z++) { + for (int x = 0; x < highest; x++, src++) { + int alpha0 = src[0] >> ALPHA_OFFSET; + int alpha1 = src[1] >> ALPHA_OFFSET; + int alpha2 = src[size] >> ALPHA_OFFSET; + int alpha3 = src[offset3] >> ALPHA_OFFSET; + int alpha4 = src[area] >> ALPHA_OFFSET; + int alpha5 = src[offset5] >> ALPHA_OFFSET; + int alpha6 = src[offset6] >> ALPHA_OFFSET; + int alpha7 = src[offset7] >> ALPHA_OFFSET; + int alphaTotal = alpha0 + alpha1 + alpha2 + alpha3 + alpha4 + alpha5 + alpha6 + alpha7; + if (alphaTotal == 0 || alphaTotal == MAX_ALPHA_TOTAL) { + continue; // no corners set/all corners set + } + + } + src++; + } + src += size; + } + + buffer = new VoxelBuffer(vertices, indices); + } + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); + return STOP_RECURSION; +} + void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod) { // copy the previous buffers @@ -1509,6 +1591,9 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, heightfieldRegionVisitor.regionBounds); data.guide(heightfieldUpdateVisitor); + + VoxelAugmentVisitor voxelAugmentVisitor(lod); + data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); } class SpannerSimulateVisitor : public SpannerVisitor { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 45496b14a1..920d95a1a7 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -39,6 +39,7 @@ public: const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } + const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } void simulate(float deltaTime); void render(); @@ -61,6 +62,7 @@ private: AttributePointer _pointBufferAttribute; AttributePointer _heightfieldBufferAttribute; + AttributePointer _voxelBufferAttribute; MetavoxelLOD _lod; QReadWriteLock _lodLock; @@ -212,6 +214,31 @@ private: QVector _buffers; }; +/// Describes contents of a vertex in a voxel buffer. +class VoxelVertex { +public: + glm::vec3 position; + quint8 color[3]; + quint8 normal[3]; +}; + +/// Contains the information necessary to render a voxel block. +class VoxelBuffer : public BufferData { +public: + + VoxelBuffer(const QVector& vertices, const QVector& indices, + const QVector& materials = QVector()); + + virtual void render(bool cursor = false); + +private: + + QVector _vertices; + QVector _indices; + QVector _materials; + QVector _networkTextures; +}; + /// A client-side attribute that stores renderable buffers. class BufferDataAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 8916e98a43..6888e56769 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -649,6 +649,11 @@ VoxelColorBoxEdit::VoxelColorBoxEdit(const Box& region, float granularity, const color(color) { } +const int VOXEL_BLOCK_SIZE = 16; +const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1; +const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES; +const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES; + class VoxelColorBoxEditVisitor : public MetavoxelVisitor { public: @@ -659,18 +664,49 @@ public: private: const VoxelColorBoxEdit& _edit; + float _blockSize; }; VoxelColorBoxEditVisitor::VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute(), QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute()), - _edit(edit) { + _edit(edit), + _blockSize(edit.granularity * VOXEL_BLOCK_SIZE) { } int VoxelColorBoxEditVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { + Box bounds = info.getBounds(); + if (!bounds.intersects(_edit.region)) { + return STOP_RECURSION; + } + if (!info.size > _blockSize) { return DEFAULT_ORDER; } + VoxelColorDataPointer pointer = info.inputValues.at(0).getInlineValue(); + QVector contents = (pointer && pointer->getSize() == VOXEL_BLOCK_SAMPLES) ? pointer->getContents() : + QVector(VOXEL_BLOCK_VOLUME); + + Box overlap = bounds.getIntersection(_edit.region); + int minX = (overlap.minimum.x - info.minimum.x) / _edit.granularity; + int minY = (overlap.minimum.y - info.minimum.y) / _edit.granularity; + int minZ = (overlap.minimum.z - info.minimum.z) / _edit.granularity; + int sizeX = (overlap.maximum.x - overlap.minimum.x) / _edit.granularity; + int sizeY = (overlap.maximum.y - overlap.minimum.y) / _edit.granularity; + int sizeZ = (overlap.maximum.z - overlap.minimum.z) / _edit.granularity; + + QRgb color = _edit.color.rgb(); + for (QRgb* destZ = contents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { + for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { + for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++) { + *destX = color; + } + } + } + + VoxelColorDataPointer newPointer(new VoxelColorData(contents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + return STOP_RECURSION; } From 96b4a1080c4e496931d03d6578541791633126a7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 27 Aug 2014 18:15:29 -0700 Subject: [PATCH 11/51] More voxel editing bits. --- interface/src/MetavoxelSystem.cpp | 48 +- interface/src/MetavoxelSystem.h | 12 +- .../metavoxels/src/AttributeRegistry.cpp | 193 +++++++- libraries/metavoxels/src/AttributeRegistry.h | 49 ++ .../metavoxels/src/MetavoxelMessages.cpp | 454 +++++++++--------- 5 files changed, 510 insertions(+), 246 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 72030d089a..ae1e0cfb73 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -978,14 +978,51 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glEnable(GL_BLEND); } -VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, +VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& materials) : _vertices(vertices), _indices(indices), + _vertexCount(vertices.size()), + _indexCount(indices.size()), + _indexBuffer(QOpenGLBuffer::IndexBuffer), _materials(materials) { } void VoxelBuffer::render(bool cursor) { + if (!_vertexBuffer.isCreated()) { + _vertexBuffer.create(); + _vertexBuffer.bind(); + _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); + _vertices.clear(); + + _indexBuffer.create(); + _indexBuffer.bind(); + _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); + _indices.clear(); + + if (!_materials.isEmpty()) { + _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 { + _vertexBuffer.bind(); + _indexBuffer.bind(); + } + VoxelPoint* point = 0; + glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); + glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); + + glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + + _vertexBuffer.release(); + _indexBuffer.release(); } BufferDataAttribute::BufferDataAttribute(const QString& name) : @@ -1506,8 +1543,9 @@ public: VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << - Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { + AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << + AttributeRegistry::getInstance()->getVoxelHermiteAttribute(), QVector() << + Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { } int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { @@ -1517,7 +1555,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { VoxelBuffer* buffer = NULL; VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue(); if (color) { - QVector vertices; + QVector vertices; QVector indices; const QVector& contents = color->getContents(); @@ -1534,7 +1572,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { const int ALPHA_OFFSET = 24; const int MAX_ALPHA_TOTAL = EIGHT_BIT_MAXIMUM * 8; for (int z = 0; z < highest; z++) { - for (int y = 0; y < highest; z++) { + for (int y = 0; y < highest; y++) { for (int x = 0; x < highest; x++, src++) { int alpha0 = src[0] >> ALPHA_OFFSET; int alpha1 = src[1] >> ALPHA_OFFSET; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 920d95a1a7..c975d76441 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -215,9 +215,9 @@ private: }; /// Describes contents of a vertex in a voxel buffer. -class VoxelVertex { +class VoxelPoint { public: - glm::vec3 position; + glm::vec3 vertex; quint8 color[3]; quint8 normal[3]; }; @@ -226,15 +226,19 @@ public: class VoxelBuffer : public BufferData { public: - VoxelBuffer(const QVector& vertices, const QVector& indices, + VoxelBuffer(const QVector& vertices, const QVector& indices, const QVector& materials = QVector()); virtual void render(bool cursor = false); private: - QVector _vertices; + QVector _vertices; QVector _indices; + int _vertexCount; + int _indexCount; + QOpenGLBuffer _vertexBuffer; + QOpenGLBuffer _indexBuffer; QVector _materials; QVector _networkTextures; }; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c8af7c05a8..c469d47aff 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -31,6 +31,7 @@ REGISTER_META_OBJECT(SharedObjectAttribute) REGISTER_META_OBJECT(SharedObjectSetAttribute) REGISTER_META_OBJECT(SpannerSetAttribute) REGISTER_META_OBJECT(VoxelColorAttribute) +REGISTER_META_OBJECT(VoxelHermiteAttribute) REGISTER_META_OBJECT(VoxelMaterialAttribute) static int attributePointerMetaTypeId = qRegisterMetaType(); @@ -56,7 +57,8 @@ AttributeRegistry::AttributeRegistry() : _heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))), _heightfieldMaterialAttribute(registerAttribute(new HeightfieldMaterialAttribute("heightfieldMaterial"))), _voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))), - _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))) { + _voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))), + _voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) { // our baseline LOD threshold is for voxels; spanners and heightfields are a different story const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f; @@ -72,6 +74,7 @@ AttributeRegistry::AttributeRegistry() : _voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); _voxelColorAttribute->setUserFacing(true); _voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); + _voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER); } static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) { @@ -1829,6 +1832,194 @@ bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRea return maxSize == 0; } +VoxelHermiteData::VoxelHermiteData(const QVector& contents, int size) : + _contents(contents), + _size(size) { +} + +VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) { + read(in, bytes); +} + +VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) { + if (!reference) { + read(in, bytes); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + reference->setEncodedDelta(in.readAligned(bytes)); + reference->setDeltaData(DataBlockPointer(this)); + _contents = reference->getContents(); + _size = reference->getSize(); + + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + QVector delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + if (delta.isEmpty()) { + return; + } + if (offsetX == 0) { + _contents = delta; + _size = sizeX; + return; + } + int minX = offsetX - 1; + int minY = offsetY - 1; + int minZ = offsetZ - 1; + const QRgb* src = delta.constData(); + int destStride = _size * EDGE_COUNT; + int destStride2 = _size * destStride; + QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT; + int srcStride = sizeX * EDGE_COUNT; + int length = srcStride * sizeof(QRgb); + for (int z = 0; z < sizeZ; z++, planeDest += destStride2) { + QRgb* dest = planeDest; + for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, length); + } + } +} + +void VoxelHermiteData::write(Bitstream& out) { + QMutexLocker locker(&_encodedMutex); + if (_encoded.isEmpty()) { + _encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents); + } + out << _encoded.size(); + out.writeAligned(_encoded); +} + +void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) { + if (!reference || reference->getSize() != _size) { + write(out); + return; + } + QMutexLocker locker(&reference->getEncodedDeltaMutex()); + if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) { + int minX = _size, minY = _size, minZ = _size; + int maxX = -1, maxY = -1, maxZ = -1; + const QRgb* src = _contents.constData(); + const QRgb* ref = reference->getContents().constData(); + for (int z = 0; z < _size; z++) { + bool differenceZ = false; + for (int y = 0; y < _size; y++) { + bool differenceY = false; + for (int x = 0; x < _size; x++) { + if (*src++ != *ref++ || *src++ != *ref++ || *src++ != *ref++) { + minX = qMin(minX, x); + maxX = qMax(maxX, x); + differenceY = differenceZ = true; + } + } + if (differenceY) { + minY = qMin(minY, y); + maxY = qMax(maxY, y); + } + } + if (differenceZ) { + minZ = qMin(minZ, z); + maxZ = qMax(maxZ, z); + } + } + QVector delta; + int sizeX = 0, sizeY = 0, sizeZ = 0; + if (maxX >= minX) { + sizeX = maxX - minX + 1; + sizeY = maxY - minY + 1; + sizeZ = maxZ - minZ + 1; + delta = QVector(sizeX * sizeY * sizeZ, 0); + QRgb* dest = delta.data(); + int srcStride = _size * EDGE_COUNT; + int srcStride2 = _size * srcStride; + const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT; + int destStride = sizeX * EDGE_COUNT; + int length = destStride * sizeof(QRgb); + for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) { + src = planeSrc; + for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, length); + } + } + } + reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta)); + reference->setDeltaData(DataBlockPointer(this)); + } + out << reference->getEncodedDelta().size(); + out.writeAligned(reference->getEncodedDelta()); +} + +void VoxelHermiteData::read(Bitstream& in, int bytes) { + int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ; + _contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ); + _size = sizeX; +} + +VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) : + InlineAttribute(name) { +} + +void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(); + } else { + *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size)); + } +} + +void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelHermiteDataPointer data = decodeInline(value); + if (data) { + data->write(out); + } else { + out << 0; + } +} + +void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + int size; + in >> size; + if (size == 0) { + *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(); + } else { + *(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData( + in, size, decodeInline(reference))); + } +} + +void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { + if (!isLeaf) { + return; + } + VoxelHermiteDataPointer data = decodeInline(value); + if (data) { + data->writeDelta(out, decodeInline(reference)); + } else { + out << 0; + } +} + +bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const { + int maxSize = 0; + for (int i = 0; i < MERGE_COUNT; i++) { + VoxelHermiteDataPointer pointer = decodeInline(children[i]); + if (pointer) { + maxSize = qMax(maxSize, pointer->getSize()); + } + } + *(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(); + return maxSize == 0; +} + SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject, const SharedObjectPointer& defaultValue) : InlineAttribute(name, defaultValue), diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index b12fcbe6a1..371bb24852 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -38,6 +38,7 @@ class MetavoxelLOD; class MetavoxelNode; class MetavoxelStreamState; class VoxelColorData; +class VoxelHermiteData; class VoxelMaterialData; typedef SharedObjectPointerTemplate AttributePointer; @@ -117,6 +118,9 @@ public: /// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute. const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; } + /// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute. + const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; } + private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); @@ -137,6 +141,7 @@ private: AttributePointer _heightfieldMaterialAttribute; AttributePointer _voxelColorAttribute; AttributePointer _voxelMaterialAttribute; + AttributePointer _voxelHermiteAttribute; }; /// Converts a value to a void pointer. @@ -728,6 +733,50 @@ public: virtual bool merge(void*& parent, void* children[], bool postRead = false) const; }; +typedef QExplicitlySharedDataPointer VoxelHermiteDataPointer; + +/// Contains a block of voxel Hermite data (positions and normals at edge crossings). +class VoxelHermiteData : public DataBlock { +public: + + static const int EDGE_COUNT = 3; + + VoxelHermiteData(const QVector& contents, int size); + VoxelHermiteData(Bitstream& in, int bytes); + VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference); + + const QVector& getContents() const { return _contents; } + + int getSize() const { return _size; } + + void write(Bitstream& out); + void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference); + +private: + + void read(Bitstream& in, int bytes); + + QVector _contents; + int _size; +}; + +/// An attribute that stores voxel Hermite data. +class VoxelHermiteAttribute : public InlineAttribute { + Q_OBJECT + +public: + + Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString()); + + virtual void read(Bitstream& in, void*& value, bool isLeaf) const; + virtual void write(Bitstream& out, void* value, bool isLeaf) const; + + virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const; + virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; + + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; +}; + /// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject). class SharedObjectAttribute : public InlineAttribute { Q_OBJECT diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 6888e56769..57e0e6348b 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -414,119 +414,36 @@ PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position, color(color) { } -class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor { -public: - - PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - PaintHeightfieldColorEdit _edit; - Box _bounds; -}; - -PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), - QVector() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()), - _edit(edit) { - - glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); - _bounds = Box(_edit.position - extents, _edit.position + extents); -} - -static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position, float radius, const QColor& color) { - HeightfieldColorDataPointer pointer = info.inputValues.at(index).getInlineValue(); - if (!pointer) { - return; - } - QByteArray contents(pointer->getContents()); - int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); - int highest = size - 1; - float heightScale = size / info.size; - - glm::vec3 center = (position - info.minimum) * heightScale; - float scaledRadius = radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - 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)highest); - int stride = size * DataBlock::COLOR_BYTES; - char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; - float squaredRadius = scaledRadius * scaledRadius; - char red = color.red(), green = color.green(), blue = color.blue(); - bool changed = false; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - char* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - dest[0] = red; - dest[1] = green; - dest[2] = blue; - changed = true; - } - } - lineDest += stride; - } - if (changed) { - HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); - info.outputValues[index] = AttributeValue(info.inputValues.at(index).getAttribute(), - encodeInline(newPointer)); - } -} - -int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) { - if (!info.getBounds().intersects(_bounds)) { - return STOP_RECURSION; - } - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - paintColor(info, 0, _edit.position, _edit.radius, _edit.color); - return STOP_RECURSION; -} - -void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldColorEditVisitor visitor(*this); - data.guide(visitor); -} - -PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, - const SharedObjectPointer& material, const QColor& averageColor) : - position(position), - radius(radius), - material(material), - averageColor(averageColor) { -} - class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor { public: - PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit); + PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color); virtual int visit(MetavoxelInfo& info); private: - PaintHeightfieldMaterialEdit _edit; + glm::vec3 _position; + float _radius; + SharedObjectPointer _material; + QColor _color; Box _bounds; }; -PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const PaintHeightfieldMaterialEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector() << - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute()), - _edit(edit) { +PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& color) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector() << + AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()), + _position(position), + _radius(radius), + _material(material), + _color(color) { - glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius); - _bounds = Box(_edit.position - extents, _edit.position + extents); + glm::vec3 extents(_radius, _radius, _radius); + _bounds = Box(_position - extents, _position + extents); } static QHash countIndices(const QByteArray& contents) { @@ -546,100 +463,153 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } - HeightfieldMaterialDataPointer pointer = info.inputValues.at(0).getInlineValue(); - if (!pointer) { - return STOP_RECURSION; - } - QVector materials = pointer->getMaterials(); - QByteArray contents(pointer->getContents()); - uchar materialIndex = 0; - if (_edit.material && static_cast(_edit.material.data())->getDiffuse().isValid()) { - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& material = materials.at(i); - if (material) { - if (material->equals(_edit.material.data())) { - materialIndex = i + 1; - break; + HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); + if (colorPointer) { + QByteArray contents(colorPointer->getContents()); + int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); + int highest = size - 1; + float heightScale = size / info.size; + + glm::vec3 center = (_position - info.minimum) * heightScale; + float scaledRadius = _radius * heightScale; + glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); + + 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)highest); + int stride = size * DataBlock::COLOR_BYTES; + char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES; + float squaredRadius = scaledRadius * scaledRadius; + char red = _color.red(), green = _color.green(), blue = _color.blue(); + bool changed = false; + for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { + char* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) { + float dx = x - center.x, dz = z - center.z; + if (dx * dx + dz * dz <= squaredRadius) { + dest[0] = red; + dest[1] = green; + dest[2] = blue; + changed = true; } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; } + lineDest += stride; } - // if nothing found, use the first empty slot or append - if (materialIndex == 0) { - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = _edit.material; - materialIndex = firstEmptyIndex + 1; - - } else if (materials.size() < EIGHT_BIT_MAXIMUM) { - materials.append(_edit.material); - materialIndex = materials.size(); - - } else { - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); + if (changed) { + HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents)); + info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), + encodeInline(newPointer)); + } + } + + HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue(); + if (materialPointer) { + QVector materials = materialPointer->getMaterials(); + QByteArray contents(materialPointer->getContents()); + uchar materialIndex = 0; + if (_material && static_cast(_material.data())->getDiffuse().isValid()) { + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& material = materials.at(i); + if (material) { + if (material->equals(_material.data())) { + materialIndex = i + 1; + break; } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (materialIndex == 0) { + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = _material; + materialIndex = firstEmptyIndex + 1; + + } else if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(_material); + materialIndex = materials.size(); + + } else { + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)materialIndex, (char)0); } - contents.replace((char)materialIndex, (char)0); } } - } - int size = glm::sqrt((float)contents.size()); - int highest = size - 1; - float heightScale = highest / info.size; - - glm::vec3 center = (_edit.position - info.minimum) * heightScale; - float scaledRadius = _edit.radius * heightScale; - glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); - - glm::vec3 start = glm::floor(center - extents); - glm::vec3 end = glm::ceil(center + extents); - - // 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)highest); - uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; - float squaredRadius = scaledRadius * scaledRadius; - bool changed = false; - QHash counts; - for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { - uchar* dest = lineDest; - for (float x = startX; x <= endX; x += 1.0f, dest++) { - float dx = x - center.x, dz = z - center.z; - if (dx * dx + dz * dz <= squaredRadius) { - *dest = materialIndex; - changed = true; + int size = glm::sqrt((float)contents.size()); + int highest = size - 1; + float heightScale = highest / info.size; + + glm::vec3 center = (_position - info.minimum) * heightScale; + float scaledRadius = _radius * heightScale; + glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius); + + 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)highest); + uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; + float squaredRadius = scaledRadius * scaledRadius; + bool changed = false; + QHash counts; + for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { + uchar* dest = lineDest; + for (float x = startX; x <= endX; x += 1.0f, dest++) { + float dx = x - center.x, dz = z - center.z; + if (dx * dx + dz * dz <= squaredRadius) { + *dest = materialIndex; + changed = true; + } } + lineDest += size; } - lineDest += size; - } - if (changed) { - // clear any unused materials - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); + if (changed) { + // clear any unused materials + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } + HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); + info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } - HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); } - paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor); return STOP_RECURSION; } +void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + PaintHeightfieldMaterialEditVisitor visitor(position, radius, SharedObjectPointer(), color); + data.guide(visitor); +} + +PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius, + const SharedObjectPointer& material, const QColor& averageColor) : + position(position), + radius(radius), + material(material), + averageColor(averageColor) { +} + void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { - PaintHeightfieldMaterialEditVisitor visitor(*this); + PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor); data.guide(visitor); } @@ -654,59 +624,98 @@ const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1; const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES; const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES; -class VoxelColorBoxEditVisitor : public MetavoxelVisitor { +class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor { public: - VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit); + VoxelMaterialBoxEditVisitor(const Box& region, float granularity, + const SharedObjectPointer& material, const QColor& color); virtual int visit(MetavoxelInfo& info); private: - const VoxelColorBoxEdit& _edit; + Box _region; + float _granularity; + SharedObjectPointer _material; + QColor _color; float _blockSize; }; -VoxelColorBoxEditVisitor::VoxelColorBoxEditVisitor(const VoxelColorBoxEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute(), - QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute()), - _edit(edit), - _blockSize(edit.granularity * VOXEL_BLOCK_SIZE) { +VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const Box& region, float granularity, + const SharedObjectPointer& material, const QColor& color) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << + AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), + _region(region), + _granularity(granularity), + _material(material), + _color(color), + _blockSize(granularity * VOXEL_BLOCK_SIZE) { } -int VoxelColorBoxEditVisitor::visit(MetavoxelInfo& info) { +int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { Box bounds = info.getBounds(); - if (!bounds.intersects(_edit.region)) { + if (!bounds.intersects(_region)) { return STOP_RECURSION; } - if (!info.size > _blockSize) { + if (info.size > _blockSize) { return DEFAULT_ORDER; } - VoxelColorDataPointer pointer = info.inputValues.at(0).getInlineValue(); - QVector contents = (pointer && pointer->getSize() == VOXEL_BLOCK_SAMPLES) ? pointer->getContents() : - QVector(VOXEL_BLOCK_VOLUME); + VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); + QVector colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + colorPointer->getContents() : QVector(VOXEL_BLOCK_VOLUME); - Box overlap = bounds.getIntersection(_edit.region); - int minX = (overlap.minimum.x - info.minimum.x) / _edit.granularity; - int minY = (overlap.minimum.y - info.minimum.y) / _edit.granularity; - int minZ = (overlap.minimum.z - info.minimum.z) / _edit.granularity; - int sizeX = (overlap.maximum.x - overlap.minimum.x) / _edit.granularity; - int sizeY = (overlap.maximum.y - overlap.minimum.y) / _edit.granularity; - int sizeZ = (overlap.maximum.z - overlap.minimum.z) / _edit.granularity; + Box overlap = info.getBounds().getIntersection(_region); + float scale = VOXEL_BLOCK_SIZE / info.size; + int minX = glm::ceil((overlap.minimum.x - info.minimum.x) * scale); + int minY = glm::ceil((overlap.minimum.y - info.minimum.y) * scale); + int minZ = glm::ceil((overlap.minimum.z - info.minimum.z) * scale); + int sizeX = (int)((overlap.maximum.x - info.minimum.x) * scale) - minX + 1; + int sizeY = (int)((overlap.maximum.y - info.minimum.y) * scale) - minY + 1; + int sizeZ = (int)((overlap.maximum.z - info.minimum.z) * scale) - minZ + 1; - QRgb color = _edit.color.rgb(); - for (QRgb* destZ = contents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + QRgb rgb = _color.rgb(); + for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++) { - *destX = color; + *destX = rgb; } } } - VoxelColorDataPointer newPointer(new VoxelColorData(contents, VOXEL_BLOCK_SAMPLES)); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(newPointer)); + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), + encodeInline(newColorPointer)); + + VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue(); + QVector hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + hermitePointer->getContents() : QVector(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT); + VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), + encodeInline(newHermitePointer)); + + VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); + QByteArray materialContents = (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + materialPointer->getContents() : QByteArray(VOXEL_BLOCK_VOLUME, 0); + + char material = 0; + for (char* destZ = materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { + for (char* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { + for (char* destX = destY, *endX = destX + sizeX; destX != endX; destX++) { + *destX = material; + } + } + } + + VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline(newMaterialPointer)); + return STOP_RECURSION; } @@ -715,8 +724,8 @@ void VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o while (!data.getBounds().contains(region)) { data.expand(); } - VoxelColorBoxEditVisitor setVisitor(*this); - data.guide(setVisitor); + VoxelMaterialBoxEditVisitor visitor(region, granularity, SharedObjectPointer(), color); + data.guide(visitor); } VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity, @@ -727,38 +736,11 @@ VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity, averageColor(averageColor) { } -class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor { -public: - - VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit); - - virtual int visit(MetavoxelInfo& info); - -private: - - const VoxelMaterialBoxEdit& _edit; -}; - -VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const VoxelMaterialBoxEdit& edit) : - MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << - AttributeRegistry::getInstance()->getVoxelColorAttribute(), QVector() << - AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << - AttributeRegistry::getInstance()->getVoxelColorAttribute()), - _edit(edit) { -} - -int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - return STOP_RECURSION; -} - void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { // expand to fit the entire edit while (!data.getBounds().contains(region)) { data.expand(); } - VoxelMaterialBoxEditVisitor setVisitor(*this); - data.guide(setVisitor); + VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor); + data.guide(visitor); } From 2b2bc9179a9c01aec41567d45bf00b431a53dc26 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 27 Aug 2014 19:09:10 -0700 Subject: [PATCH 12/51] More progress towards computing Hermite data. --- .../metavoxels/src/MetavoxelMessages.cpp | 132 ++++++++++-------- 1 file changed, 76 insertions(+), 56 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 57e0e6348b..5e2acb69c4 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -456,6 +456,57 @@ static QHash countIndices(const QByteArray& contents) { return counts; } +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { + if (!(material && static_cast(material.data())->getDiffuse().isValid())) { + return 0; + } + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& existingMaterial = materials.at(i); + if (existingMaterial) { + if (existingMaterial->equals(material.data())) { + return i + 1; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = material; + return firstEmptyIndex + 1; + } + if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(material); + return materials.size(); + } + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + uchar materialIndex = 0; + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)materialIndex, (char)0); + return materialIndex; +} + +void clearUnusedMaterials(QVector& materials, QByteArray& contents) { + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } + } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } +} + int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; @@ -509,45 +560,7 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { if (materialPointer) { QVector materials = materialPointer->getMaterials(); QByteArray contents(materialPointer->getContents()); - uchar materialIndex = 0; - if (_material && static_cast(_material.data())->getDiffuse().isValid()) { - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& material = materials.at(i); - if (material) { - if (material->equals(_material.data())) { - materialIndex = i + 1; - break; - } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; - } - } - // if nothing found, use the first empty slot or append - if (materialIndex == 0) { - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = _material; - materialIndex = firstEmptyIndex + 1; - - } else if (materials.size() < EIGHT_BIT_MAXIMUM) { - materials.append(_material); - materialIndex = materials.size(); - - } else { - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); - } - } - contents.replace((char)materialIndex, (char)0); - } - } - } + uchar materialIndex = getMaterialIndex(_material, materials, contents); int size = glm::sqrt((float)contents.size()); int highest = size - 1; float heightScale = highest / info.size; @@ -578,16 +591,7 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { lineDest += size; } if (changed) { - // clear any unused materials - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); - } - } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } + clearUnusedMaterials(materials, contents); HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials)); info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline(newPointer)); } @@ -694,6 +698,20 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue(); QVector hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ? hermitePointer->getContents() : QVector(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT); + int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT; + int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT; + + for (QRgb* destZ = hermiteContents.data() + minZ * hermiteArea + minY * hermiteSamples + + minX * VoxelHermiteData::EDGE_COUNT, *endZ = destZ + sizeZ * hermiteArea; destZ != endZ; destZ += hermiteArea) { + for (QRgb* destY = destZ, *endY = destY + sizeY * hermiteSamples; destY != endY; destY += hermiteSamples) { + for (QRgb* destX = destY, *endX = destX + sizeX * VoxelHermiteData::EDGE_COUNT; destX != endX; + destX += VoxelHermiteData::EDGE_COUNT) { + destX[0] = 0x0; + destX[1] = 0x0; + destX[2] = 0x0; + } + } + } VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES)); info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), @@ -702,18 +720,20 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); QByteArray materialContents = (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? materialPointer->getContents() : QByteArray(VOXEL_BLOCK_VOLUME, 0); - - char material = 0; - for (char* destZ = materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + QVector materials = materialPointer->getMaterials(); + + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); + for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { - for (char* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { - for (char* destX = destY, *endX = destX + sizeX; destX != endX; destX++) { - *destX = material; + for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { + for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++) { + *destX = materialIndex; } } } - VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES)); + clearUnusedMaterials(materials, materialContents); + VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials)); info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline(newMaterialPointer)); return STOP_RECURSION; From f02f95c460f1b4cc1a4942e355e10be711282ed8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 28 Aug 2014 14:53:48 -0700 Subject: [PATCH 13/51] Hermite data computation. --- interface/src/MetavoxelSystem.cpp | 30 +++++- .../metavoxels/src/MetavoxelMessages.cpp | 94 +++++++++++++++---- 2 files changed, 101 insertions(+), 23 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index ae1e0cfb73..8dbc472e98 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1019,7 +1019,13 @@ void VoxelBuffer::render(bool cursor) { glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + //glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + + glPointSize(5.0f); + + glDrawArrays(GL_POINTS, 0, _vertexCount); + + glPointSize(1.0f); _vertexBuffer.release(); _indexBuffer.release(); @@ -1554,7 +1560,9 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } VoxelBuffer* buffer = NULL; VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue(); - if (color) { + VoxelMaterialDataPointer material = info.inputValues.at(1).getInlineValue(); + VoxelHermiteDataPointer hermite = info.inputValues.at(2).getInlineValue(); + if (color && material && hermite) { QVector vertices; QVector indices; @@ -1569,6 +1577,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { const QRgb* src = contents.constData(); + float scale = info.size / highest; const int ALPHA_OFFSET = 24; const int MAX_ALPHA_TOTAL = EIGHT_BIT_MAXIMUM * 8; for (int z = 0; z < highest; z++) { @@ -1586,14 +1595,16 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { if (alphaTotal == 0 || alphaTotal == MAX_ALPHA_TOTAL) { continue; // no corners set/all corners set } - + VoxelPoint point = { info.minimum + glm::vec3(x + 0.5f, y + 0.5f, z + 0.5f) * scale, + { 0, 0, 0 }, { 0, 127, 0} }; + vertices.append(point); } src++; } src += size; } - buffer = new VoxelBuffer(vertices, indices); + buffer = new VoxelBuffer(vertices, indices, material->getMaterials()); } info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer))); return STOP_RECURSION; @@ -1817,6 +1828,17 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); + data.guide(voxelRenderVisitor); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); } ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 5e2acb69c4..eccd057412 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -674,12 +674,14 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { Box overlap = info.getBounds().getIntersection(_region); float scale = VOXEL_BLOCK_SIZE / info.size; - int minX = glm::ceil((overlap.minimum.x - info.minimum.x) * scale); - int minY = glm::ceil((overlap.minimum.y - info.minimum.y) * scale); - int minZ = glm::ceil((overlap.minimum.z - info.minimum.z) * scale); - int sizeX = (int)((overlap.maximum.x - info.minimum.x) * scale) - minX + 1; - int sizeY = (int)((overlap.maximum.y - info.minimum.y) * scale) - minY + 1; - int sizeZ = (int)((overlap.maximum.z - info.minimum.z) * scale) - minZ + 1; + overlap.minimum = (overlap.minimum - info.minimum) * scale; + overlap.maximum = (overlap.maximum - info.minimum) * scale; + int minX = glm::ceil(overlap.minimum.x); + int minY = glm::ceil(overlap.minimum.y); + int minZ = glm::ceil(overlap.minimum.z); + int sizeX = (int)overlap.maximum.x - minX + 1; + int sizeY = (int)overlap.maximum.y - minY + 1; + int sizeZ = (int)overlap.maximum.z - minZ + 1; QRgb rgb = _color.rgb(); for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, @@ -701,27 +703,81 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT; int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT; - for (QRgb* destZ = hermiteContents.data() + minZ * hermiteArea + minY * hermiteSamples + - minX * VoxelHermiteData::EDGE_COUNT, *endZ = destZ + sizeZ * hermiteArea; destZ != endZ; destZ += hermiteArea) { - for (QRgb* destY = destZ, *endY = destY + sizeY * hermiteSamples; destY != endY; destY += hermiteSamples) { - for (QRgb* destX = destY, *endX = destX + sizeX * VoxelHermiteData::EDGE_COUNT; destX != endX; - destX += VoxelHermiteData::EDGE_COUNT) { - destX[0] = 0x0; - destX[1] = 0x0; - destX[2] = 0x0; + int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ; + int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ; + if (minX > 0) { + hermiteMinX--; + hermiteSizeX++; + } + if (minY > 0) { + hermiteMinY--; + hermiteSizeY++; + } + if (minZ > 0) { + hermiteMinZ--; + hermiteSizeZ++; + } + const int NORMAL_MAX = 127; + QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples + + hermiteMinX * VoxelHermiteData::EDGE_COUNT; + for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) { + QRgb* hermiteDestY = hermiteDestZ; + for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) { + QRgb* hermiteDestX = hermiteDestY; + for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, + hermiteDestX += VoxelHermiteData::EDGE_COUNT) { + hermiteDestX[0] = 0x0; + if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + if (qAlpha(color[0]) != qAlpha(color[1])) { + if (x == hermiteMinX) { + hermiteDestX[0] = qRgba(-NORMAL_MAX, 0, 0, (overlap.minimum.x - x) * EIGHT_BIT_MAXIMUM); + } else { + hermiteDestX[0] = qRgba(NORMAL_MAX, 0, 0, (overlap.maximum.x - x) * EIGHT_BIT_MAXIMUM); + } + } + } + hermiteDestX[1] = 0x0; + if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + if (qAlpha(color[0]) != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { + if (y == hermiteMinY) { + hermiteDestX[1] = qRgba(0, -NORMAL_MAX, 0, (overlap.minimum.y - y) * EIGHT_BIT_MAXIMUM); + } else { + hermiteDestX[1] = qRgba(0, NORMAL_MAX, 0, (overlap.maximum.y - y) * EIGHT_BIT_MAXIMUM); + } + } + } + hermiteDestX[2] = 0x0; + if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + if (qAlpha(color[0]) != qAlpha(color[VOXEL_BLOCK_AREA])) { + if (z == hermiteMinZ) { + hermiteDestX[2] = qRgba(0, 0, -NORMAL_MAX, (overlap.minimum.z - z) * EIGHT_BIT_MAXIMUM); + } else { + hermiteDestX[2] = qRgba(0, 0, NORMAL_MAX, (overlap.maximum.z - z) * EIGHT_BIT_MAXIMUM); + } + } + } } } - } - + } + VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES)); info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), encodeInline(newHermitePointer)); VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); - QByteArray materialContents = (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? - materialPointer->getContents() : QByteArray(VOXEL_BLOCK_VOLUME, 0); - QVector materials = materialPointer->getMaterials(); + QByteArray materialContents; + QVector materials; + if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) { + materialContents = materialPointer->getContents(); + materials = materialPointer->getMaterials(); + } else { + materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0); + } + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { From 78a03fb31adb8c69d2f8499efa126a971ef98227 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 28 Aug 2014 20:01:55 -0700 Subject: [PATCH 14/51] More progress toward dual contour rendering. --- interface/src/MetavoxelSystem.cpp | 154 +++++++++++++++--- .../metavoxels/src/AttributeRegistry.cpp | 2 +- .../metavoxels/src/MetavoxelMessages.cpp | 2 +- 3 files changed, 137 insertions(+), 21 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 8dbc472e98..d8ec1da0de 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1014,14 +1014,15 @@ void VoxelBuffer::render(bool cursor) { _vertexBuffer.bind(); _indexBuffer.bind(); } + VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - //glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + // glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); - glPointSize(5.0f); + glPointSize(3.0f); glDrawArrays(GL_POINTS, 0, _vertexCount); @@ -1569,7 +1570,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { const QVector& contents = color->getContents(); int size = color->getSize(); int area = size * size; - int highest = size - 1; int offset3 = size + 1; int offset5 = area + 1; int offset6 = area + size; @@ -1577,31 +1577,136 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { const QRgb* src = contents.constData(); + int expanded = size + 1; + QVector lineIndices(expanded, -1); + QVector lastLineIndices(expanded, -1); + QVector planeIndices(expanded * expanded, -1); + QVector lastPlaneIndices(expanded * expanded, -1); + + float highest = size - 1.0f; float scale = info.size / highest; const int ALPHA_OFFSET = 24; - const int MAX_ALPHA_TOTAL = EIGHT_BIT_MAXIMUM * 8; - for (int z = 0; z < highest; z++) { - for (int y = 0; y < highest; y++) { - for (int x = 0; x < highest; x++, src++) { + for (int z = 0; z < expanded; z++) { + for (int y = 0; y < expanded; y++) { + int lastIndex; + for (int x = 0; x < expanded; x++) { int alpha0 = src[0] >> ALPHA_OFFSET; - int alpha1 = src[1] >> ALPHA_OFFSET; - int alpha2 = src[size] >> ALPHA_OFFSET; - int alpha3 = src[offset3] >> ALPHA_OFFSET; - int alpha4 = src[area] >> ALPHA_OFFSET; - int alpha5 = src[offset5] >> ALPHA_OFFSET; - int alpha6 = src[offset6] >> ALPHA_OFFSET; - int alpha7 = src[offset7] >> ALPHA_OFFSET; - int alphaTotal = alpha0 + alpha1 + alpha2 + alpha3 + alpha4 + alpha5 + alpha6 + alpha7; - if (alphaTotal == 0 || alphaTotal == MAX_ALPHA_TOTAL) { + int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; + int alphaTotal = alpha0; + int possibleTotal = EIGHT_BIT_MAXIMUM; + if (x != 0 && x != size) { + alphaTotal += (alpha1 = src[1] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (y != 0 && y != size) { + alphaTotal += (src[offset3] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (z != 0 && z != size) { + alphaTotal += (src[offset7] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + } + if (z != 0 && z != size) { + alphaTotal += (src[offset5] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + } + if (y != 0 && y != size) { + alphaTotal += (alpha2 = src[size] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (z != 0 && z != size) { + alphaTotal += (src[offset6] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + } + if (z != 0 && z != size) { + alphaTotal += (alpha4 = src[area] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + + bool generateQuad = (x != 0 && y != 0 && z != 0); + if (generateQuad) { + src++; + } + if (z == 0) { + qDebug() << "blerp" << x << y << z << alphaTotal << possibleTotal; + } + if (alphaTotal == 0 || alphaTotal == possibleTotal) { continue; // no corners set/all corners set } - VoxelPoint point = { info.minimum + glm::vec3(x + 0.5f, y + 0.5f, z + 0.5f) * scale, + VoxelPoint point = { info.minimum + glm::vec3(glm::clamp(x - 0.5f, 0.0f, highest), + glm::clamp(y - 0.5f, 0.0f, highest), glm::clamp(z - 0.5f, 0.0f, highest)) * scale, { 0, 0, 0 }, { 0, 127, 0} }; + int index = vertices.size(); vertices.append(point); + + if (generateQuad) { + if (alpha0 != alpha1) { + indices.append(index); + int index1 = lastLineIndices.at(x); + int index2 = lastPlaneIndices.at((y - 1) * expanded + x); + int index3 = lastPlaneIndices.at(y * expanded + x); + if (index1 == -1 || index2 == -1 || index3 == -1) { + qDebug() << index1 << index2 << index3 << x << y << z; + } + if (alpha0 == 0) { + indices.append(index3); + indices.append(index2); + indices.append(index1); + } else { + indices.append(index1); + indices.append(index2); + indices.append(index3); + } + } + + if (alpha0 != alpha2) { + indices.append(index); + int index1 = lastIndex; + int index2 = lastPlaneIndices.at(y * expanded + x - 1); + int index3 = lastPlaneIndices.at(y * expanded + x); + if (index1 == -1 || index2 == -1 || index3 == -1) { + qDebug() << index1 << index2 << index3 << x << y << z; + } + if (alpha0 == 0) { + indices.append(index1); + indices.append(index2); + indices.append(index3); + } else { + indices.append(index3); + indices.append(index2); + indices.append(index1); + } + } + + if (alpha0 != alpha4) { + indices.append(index); + int index1 = lastIndex; + int index2 = lastLineIndices.at(x - 1); + int index3 = lastLineIndices.at(x); + if (index1 == -1 || index2 == -1 || index3 == -1) { + qDebug() << index1 << index2 << index3 << x << y << z; + } + if (alpha0 == 0) { + indices.append(index3); + indices.append(index2); + indices.append(index1); + } else { + indices.append(index1); + indices.append(index2); + indices.append(index3); + } + } + } + lastIndex = index; + lineIndices[x] = index; + planeIndices[y * expanded + x] = index; } - src++; + lineIndices.swap(lastLineIndices); } - src += size; + planeIndices.swap(lastPlaneIndices); } buffer = new VoxelBuffer(vertices, indices, material->getMaterials()); @@ -1630,6 +1735,13 @@ void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const data.setRoot(heightfieldBufferAttribute, root); root->incrementReferenceCount(); } + const AttributePointer& voxelBufferAttribute = + Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); + root = expandedPrevious.getRoot(voxelBufferAttribute); + if (root) { + data.setRoot(voxelBufferAttribute, root); + root->incrementReferenceCount(); + } PointAugmentVisitor pointAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, pointAugmentVisitor); @@ -1833,9 +1945,13 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); + glEnable(GL_CULL_FACE); + BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); + glDisable(GL_CULL_FACE); + glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index c469d47aff..db684bf1c5 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1926,7 +1926,7 @@ void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& sizeX = maxX - minX + 1; sizeY = maxY - minY + 1; sizeZ = maxZ - minZ + 1; - delta = QVector(sizeX * sizeY * sizeZ, 0); + delta = QVector(sizeX * sizeY * sizeZ * EDGE_COUNT, 0); QRgb* dest = delta.data(); int srcStride = _size * EDGE_COUNT; int srcStride2 = _size * srcStride; diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index eccd057412..24f32f1989 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -683,7 +683,7 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { int sizeY = (int)overlap.maximum.y - minY + 1; int sizeZ = (int)overlap.maximum.z - minZ + 1; - QRgb rgb = _color.rgb(); + QRgb rgb = _color.rgba(); for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA) { for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) { From 120563951db3575d7f03896fea0c65deace72eae Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 29 Aug 2014 12:10:25 -0700 Subject: [PATCH 15/51] Fixed bug in edge vertex generation. --- interface/src/MetavoxelSystem.cpp | 37 +++++++------------ .../metavoxels/src/MetavoxelMessages.cpp | 30 ++++++--------- 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d8ec1da0de..6906c0bf7e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1020,13 +1020,7 @@ void VoxelBuffer::render(bool cursor) { glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); - // glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); - - glPointSize(3.0f); - - glDrawArrays(GL_POINTS, 0, _vertexCount); - - glPointSize(1.0f); + glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); _vertexBuffer.release(); _indexBuffer.release(); @@ -1575,7 +1569,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int offset6 = area + size; int offset7 = area + size + 1; - const QRgb* src = contents.constData(); + const QRgb* srcZ = contents.constData(); int expanded = size + 1; QVector lineIndices(expanded, -1); @@ -1587,8 +1581,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { float scale = info.size / highest; const int ALPHA_OFFSET = 24; for (int z = 0; z < expanded; z++) { + const QRgb* srcY = srcZ; for (int y = 0; y < expanded; y++) { int lastIndex; + const QRgb* src = srcY; for (int x = 0; x < expanded; x++) { int alpha0 = src[0] >> ALPHA_OFFSET; int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; @@ -1626,13 +1622,9 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { possibleTotal += EIGHT_BIT_MAXIMUM; } - bool generateQuad = (x != 0 && y != 0 && z != 0); - if (generateQuad) { + if (x != 0) { src++; } - if (z == 0) { - qDebug() << "blerp" << x << y << z << alphaTotal << possibleTotal; - } if (alphaTotal == 0 || alphaTotal == possibleTotal) { continue; // no corners set/all corners set } @@ -1642,15 +1634,12 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index = vertices.size(); vertices.append(point); - if (generateQuad) { + if (x != 0 && y != 0 && z != 0) { if (alpha0 != alpha1) { indices.append(index); int index1 = lastLineIndices.at(x); int index2 = lastPlaneIndices.at((y - 1) * expanded + x); int index3 = lastPlaneIndices.at(y * expanded + x); - if (index1 == -1 || index2 == -1 || index3 == -1) { - qDebug() << index1 << index2 << index3 << x << y << z; - } if (alpha0 == 0) { indices.append(index3); indices.append(index2); @@ -1667,9 +1656,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index1 = lastIndex; int index2 = lastPlaneIndices.at(y * expanded + x - 1); int index3 = lastPlaneIndices.at(y * expanded + x); - if (index1 == -1 || index2 == -1 || index3 == -1) { - qDebug() << index1 << index2 << index3 << x << y << z; - } if (alpha0 == 0) { indices.append(index1); indices.append(index2); @@ -1686,9 +1672,6 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index1 = lastIndex; int index2 = lastLineIndices.at(x - 1); int index3 = lastLineIndices.at(x); - if (index1 == -1 || index2 == -1 || index3 == -1) { - qDebug() << index1 << index2 << index3 << x << y << z; - } if (alpha0 == 0) { indices.append(index3); indices.append(index2); @@ -1705,8 +1688,16 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { planeIndices[y * expanded + x] = index; } lineIndices.swap(lastLineIndices); + + if (y != 0) { + srcY += size; + } } planeIndices.swap(lastPlaneIndices); + + if (z != 0) { + srcZ += area; + } } buffer = new VoxelBuffer(vertices, indices, material->getMaterials()); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 24f32f1989..da886cbf5d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -729,34 +729,28 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { hermiteDestX[0] = 0x0; if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) { const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - if (qAlpha(color[0]) != qAlpha(color[1])) { - if (x == hermiteMinX) { - hermiteDestX[0] = qRgba(-NORMAL_MAX, 0, 0, (overlap.minimum.x - x) * EIGHT_BIT_MAXIMUM); - } else { - hermiteDestX[0] = qRgba(NORMAL_MAX, 0, 0, (overlap.maximum.x - x) * EIGHT_BIT_MAXIMUM); - } + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[1])) { + hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, + ((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM); } } hermiteDestX[1] = 0x0; if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) { const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - if (qAlpha(color[0]) != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { - if (y == hermiteMinY) { - hermiteDestX[1] = qRgba(0, -NORMAL_MAX, 0, (overlap.minimum.y - y) * EIGHT_BIT_MAXIMUM); - } else { - hermiteDestX[1] = qRgba(0, NORMAL_MAX, 0, (overlap.maximum.y - y) * EIGHT_BIT_MAXIMUM); - } + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { + hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, + ((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM); } } hermiteDestX[2] = 0x0; if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) { const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; - if (qAlpha(color[0]) != qAlpha(color[VOXEL_BLOCK_AREA])) { - if (z == hermiteMinZ) { - hermiteDestX[2] = qRgba(0, 0, -NORMAL_MAX, (overlap.minimum.z - z) * EIGHT_BIT_MAXIMUM); - } else { - hermiteDestX[2] = qRgba(0, 0, NORMAL_MAX, (overlap.maximum.z - z) * EIGHT_BIT_MAXIMUM); - } + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) { + hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, + ((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM); } } } From fa06bef556346c679491a43d4daa9b9ce9207941 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 29 Aug 2014 18:00:11 -0700 Subject: [PATCH 16/51] More dual contour bits. --- interface/src/MetavoxelSystem.cpp | 302 +++++++++++++++++++++++++----- interface/src/MetavoxelSystem.h | 2 +- 2 files changed, 259 insertions(+), 45 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 6906c0bf7e..07c961fbd3 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -144,7 +144,8 @@ RayHeightfieldIntersectionVisitor::RayHeightfieldIntersectionVisitor(const glm:: intersectionDistance(FLT_MAX) { } -static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / 255.0f; +static const int EIGHT_BIT_MAXIMUM = 255; +static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; int RayHeightfieldIntersectionVisitor::visit(MetavoxelInfo& info, float distance) { if (!info.isLeaf) { @@ -1267,8 +1268,6 @@ HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const _intersections(intersections) { } -const int EIGHT_BIT_MAXIMUM = 255; - int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { Box bounds = info.getBounds(); const Box& heightBounds = _buffer->getHeightBounds(); @@ -1549,6 +1548,14 @@ VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { } +class EdgeCrossing { +public: + glm::vec3 point; + glm::vec3 normal; + QRgb color; + char material; +}; + int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; @@ -1561,7 +1568,9 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; - const QVector& contents = color->getContents(); + const QVector& colorContents = color->getContents(); + const QByteArray& materialContents = material->getContents(); + const QVector& hermiteContents = hermite->getContents(); int size = color->getSize(); int area = size * size; int offset3 = size + 1; @@ -1569,7 +1578,12 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int offset6 = area + size; int offset7 = area + size + 1; - const QRgb* srcZ = contents.constData(); + const QRgb* colorZ = colorContents.constData(); + const QRgb* hermiteData = hermiteContents.constData(); + int hermiteStride = hermite->getSize() * VoxelHermiteData::EDGE_COUNT; + int hermiteArea = hermiteStride * hermite->getSize(); + + const char* materialData = materialContents.constData(); int expanded = size + 1; QVector lineIndices(expanded, -1); @@ -1577,60 +1591,256 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector planeIndices(expanded * expanded, -1); QVector lastPlaneIndices(expanded * expanded, -1); + const int EDGES_PER_CUBE = 12; + EdgeCrossing crossings[EDGES_PER_CUBE]; + float highest = size - 1.0f; float scale = info.size / highest; const int ALPHA_OFFSET = 24; for (int z = 0; z < expanded; z++) { - const QRgb* srcY = srcZ; + const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { int lastIndex; - const QRgb* src = srcY; + const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { - int alpha0 = src[0] >> ALPHA_OFFSET; + int alpha0 = colorX[0] >> ALPHA_OFFSET; int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; int alphaTotal = alpha0; int possibleTotal = EIGHT_BIT_MAXIMUM; - if (x != 0 && x != size) { - alphaTotal += (alpha1 = src[1] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (y != 0 && y != size) { - alphaTotal += (src[offset3] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (z != 0 && z != size) { - alphaTotal += (src[offset7] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - if (z != 0 && z != size) { - alphaTotal += (src[offset5] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - if (y != 0 && y != size) { - alphaTotal += (alpha2 = src[size] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - - if (z != 0 && z != size) { - alphaTotal += (src[offset6] >> ALPHA_OFFSET); - possibleTotal += EIGHT_BIT_MAXIMUM; - } - } - if (z != 0 && z != size) { - alphaTotal += (alpha4 = src[area] >> ALPHA_OFFSET); + bool middleX = (x != 0 && x != size); + bool middleY = (y != 0 && y != size); + bool middleZ = (z != 0 && z != size); + if (middleZ) { + alphaTotal += (alpha4 = colorX[area] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } - if (x != 0) { - src++; + int alpha5 = alpha4, alpha6 = alpha4; + if (middleY) { + alphaTotal += (alpha2 = colorX[size] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (middleZ) { + alphaTotal += (alpha6 = colorX[offset6] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + } + + int alpha3 = alpha2, alpha7 = alpha6; + if (middleX) { + alphaTotal += (alpha1 = colorX[1] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (middleY) { + alphaTotal += (alpha3 = colorX[offset3] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + + if (middleZ) { + alphaTotal += (alpha7 = colorX[offset7] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } + } + if (middleZ) { + alphaTotal += (alpha5 = colorX[offset5] >> ALPHA_OFFSET); + possibleTotal += EIGHT_BIT_MAXIMUM; + } } if (alphaTotal == 0 || alphaTotal == possibleTotal) { + if (x != 0) { + colorX++; + } continue; // no corners set/all corners set } - VoxelPoint point = { info.minimum + glm::vec3(glm::clamp(x - 0.5f, 0.0f, highest), - glm::clamp(y - 0.5f, 0.0f, highest), glm::clamp(z - 0.5f, 0.0f, highest)) * scale, - { 0, 0, 0 }, { 0, 127, 0} }; + int clampedX = qMax(x - 1, 0), clampedY = qMax(y - 1, 0), clampedZ = qMax(z - 1, 0); + const QRgb* hermiteBase = hermiteData + clampedZ * hermiteArea + clampedY * hermiteStride + + clampedX * VoxelHermiteData::EDGE_COUNT; + const char* materialBase = materialData + clampedZ * area + clampedY * size + clampedX; + int crossingCount = 0; + if (middleX) { + if (alpha0 != alpha1) { + QRgb hermite = hermiteBase[0]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha0 == 0) { + crossing.color = colorX[1]; + crossing.material = materialBase[1]; + } else { + crossing.color = colorX[0]; + crossing.material = materialBase[0]; + } + crossing.point = glm::vec3(qAlpha(hermite), 0.0f, 0.0f); + } + if (middleY) { + if (alpha1 != alpha3) { + QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 1]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha1 == 0) { + crossing.color = colorX[offset3]; + crossing.material = materialBase[offset3]; + } else { + crossing.color = colorX[1]; + crossing.material = materialBase[1]; + } + crossing.point = glm::vec3(EIGHT_BIT_MAXIMUM, qAlpha(hermite), 0.0f); + } + if (alpha2 != alpha3) { + QRgb hermite = hermiteBase[hermiteStride]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha2 == 0) { + crossing.color = colorX[offset3]; + crossing.material = materialBase[offset3]; + } else { + crossing.color = colorX[size]; + crossing.material = materialBase[size]; + } + crossing.point = glm::vec3(qAlpha(hermite), EIGHT_BIT_MAXIMUM, 0.0f); + } + if (middleZ) { + if (alpha3 != alpha7) { + QRgb hermite = hermiteBase[hermiteStride + VoxelHermiteData::EDGE_COUNT + 2]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha3 == 0) { + crossing.color = colorX[offset7]; + crossing.material = materialBase[offset7]; + } else { + crossing.color = colorX[offset3]; + crossing.material = materialBase[offset3]; + } + crossing.point = glm::vec3(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, qAlpha(hermite)); + } + if (alpha5 != alpha7) { + QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha5 == 0) { + crossing.color = colorX[offset7]; + crossing.material = materialBase[offset7]; + } else { + crossing.color = colorX[offset5]; + crossing.material = materialBase[offset5]; + } + crossing.point = glm::vec3(EIGHT_BIT_MAXIMUM, qAlpha(hermite), EIGHT_BIT_MAXIMUM); + } + if (alpha6 != alpha7) { + QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha6 == 0) { + crossing.color = colorX[offset7]; + crossing.material = materialBase[offset7]; + } else { + crossing.color = colorX[offset6]; + crossing.material = materialBase[offset6]; + } + crossing.point = glm::vec3(qAlpha(hermite), EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); + } + } + } + if (middleZ) { + if (alpha1 != alpha5) { + QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 2]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha1 == 0) { + crossing.color = colorX[offset5]; + crossing.material = materialBase[offset5]; + } else { + crossing.color = colorX[1]; + crossing.material = materialBase[1]; + } + crossing.point = glm::vec3(EIGHT_BIT_MAXIMUM, 0.0f, qAlpha(hermite)); + } + if (alpha4 != alpha5) { + QRgb hermite = hermiteBase[hermiteArea]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha4 == 0) { + crossing.color = colorX[offset5]; + crossing.material = materialBase[offset5]; + } else { + crossing.color = colorX[area]; + crossing.material = materialBase[area]; + } + crossing.point = glm::vec3(qAlpha(hermite), 0.0f, EIGHT_BIT_MAXIMUM); + } + } + } + if (middleY) { + if (alpha0 != alpha2) { + QRgb hermite = hermiteBase[1]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha0 == 0) { + crossing.color = colorX[size]; + crossing.material = materialBase[size]; + } else { + crossing.color = colorX[0]; + crossing.material = materialBase[0]; + } + crossing.point = glm::vec3(0.0f, qAlpha(hermite), 0.0f); + } + if (middleZ) { + if (alpha2 != alpha6) { + QRgb hermite = hermiteBase[hermiteStride + 2]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha2 == 0) { + crossing.color = colorX[offset6]; + crossing.material = materialBase[offset6]; + } else { + crossing.color = colorX[size]; + crossing.material = materialBase[size]; + } + crossing.point = glm::vec3(0.0f, EIGHT_BIT_MAXIMUM, qAlpha(hermite)); + } + if (alpha4 != alpha6) { + QRgb hermite = hermiteBase[hermiteArea + 1]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha4 == 0) { + crossing.color = colorX[offset6]; + crossing.material = materialBase[offset6]; + } else { + crossing.color = colorX[area]; + crossing.material = materialBase[area]; + } + crossing.point = glm::vec3(0.0f, qAlpha(hermite), EIGHT_BIT_MAXIMUM); + } + } + } + if (middleZ && alpha0 != alpha4) { + QRgb hermite = hermiteBase[2]; + EdgeCrossing& crossing = crossings[crossingCount++]; + crossing.normal = unpackNormal(hermite); + if (alpha0 == 0) { + crossing.color = colorX[area]; + crossing.material = materialBase[area]; + } else { + crossing.color = colorX[0]; + crossing.material = materialBase[0]; + } + crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite)); + } + glm::vec3 center; + glm::vec3 normal; + int red = 0, green = 0, blue = 0; + for (int i = 0; i < crossingCount; i++) { + const EdgeCrossing& crossing = crossings[i]; + center += crossing.point; + normal += crossing.normal; + red += qRed(crossing.color); + green += qGreen(crossing.color); + blue += qBlue(crossing.color); + } + normal = glm::normalize(normal); + center /= crossingCount; + VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + + center * EIGHT_BIT_MAXIMUM_RECIPROCAL) * scale, + { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, + { (char)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) } }; int index = vertices.size(); vertices.append(point); @@ -1686,17 +1896,21 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { lastIndex = index; lineIndices[x] = index; planeIndices[y * expanded + x] = index; + + if (x != 0) { + colorX++; + } } lineIndices.swap(lastLineIndices); if (y != 0) { - srcY += size; + colorY += size; } } planeIndices.swap(lastPlaneIndices); if (z != 0) { - srcZ += area; + colorZ += area; } } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index c975d76441..3be00543c2 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -219,7 +219,7 @@ class VoxelPoint { public: glm::vec3 vertex; quint8 color[3]; - quint8 normal[3]; + char normal[3]; }; /// Contains the information necessary to render a voxel block. From ece3390b4eabaa729daea028785876032c3f37f4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Sep 2014 22:14:28 -0700 Subject: [PATCH 17/51] Fix bug with dropping hifi images not working on Windows URLs on Windows included three forward-slashes, ex `file:///C:...`. Using `toLocalFile()` correctly stringifies the location. --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 10ae4b0303..7c8991d5a6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1315,7 +1315,7 @@ void Application::dropEvent(QDropEvent *event) { const QMimeData *mimeData = event->mimeData(); foreach (QUrl url, mimeData->urls()) { if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) { - snapshotPath = url.url().remove("file://"); + snapshotPath = url.toLocalFile(); break; } } From 33faffd9d40d5eb76123e11b2f248b0f1bc52251 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Sep 2014 17:45:37 -0700 Subject: [PATCH 18/51] Sphere tools for voxel editing. --- interface/src/ui/MetavoxelEditor.cpp | 109 ++++++++ interface/src/ui/MetavoxelEditor.h | 71 +++++ .../metavoxels/src/AttributeRegistry.cpp | 4 + libraries/metavoxels/src/AttributeRegistry.h | 3 + .../metavoxels/src/MetavoxelMessages.cpp | 264 ++++++++++++++++++ libraries/metavoxels/src/MetavoxelMessages.h | 39 +++ 6 files changed, 490 insertions(+) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 5702db39a2..b9e262f3fc 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -126,6 +126,8 @@ MetavoxelEditor::MetavoxelEditor() : addTool(new EraseHeightfieldTool(this)); addTool(new VoxelColorBoxTool(this)); addTool(new VoxelMaterialBoxTool(this)); + addTool(new VoxelColorSphereTool(this)); + addTool(new VoxelMaterialSphereTool(this)); updateAttributes(); @@ -1282,3 +1284,110 @@ void VoxelMaterialBoxTool::updateTexture() { MaterialObject* material = static_cast(_materialEditor->getObject().data()); _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); } + +SphereTool::SphereTool(MetavoxelEditor* editor, const QString& name) : + MetavoxelTool(editor, name, false, true) { + + QWidget* widget = new QWidget(); + widget->setLayout(_form = new QFormLayout()); + layout()->addWidget(widget); + + _form->addRow("Radius:", _radius = new QDoubleSpinBox()); + _radius->setSingleStep(0.01); + _radius->setMaximum(FLT_MAX); + _radius->setValue(1.0); +} + +void SphereTool::render() { + if (Application::getInstance()->isMouseHidden()) { + return; + } + glm::quat rotation = _editor->getGridRotation(); + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); + float position = _editor->getGridPosition(); + if (glm::abs(rayDirection.z) < EPSILON) { + return; + } + float distance = (position - rayOrigin.z) / rayDirection.z; + _position = Application::getInstance()->getMouseRayOrigin() + + Application::getInstance()->getMouseRayDirection() * distance; + + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + + const float CURSOR_ALPHA = 0.5f; + QColor color = getColor(); + glColor4f(color.redF(), color.greenF(), color.blueF(), CURSOR_ALPHA); + + glEnable(GL_CULL_FACE); + + glutSolidSphere(_radius->value(), 10, 10); + + glDisable(GL_CULL_FACE); + + glPopMatrix(); +} + +bool SphereTool::eventFilter(QObject* watched, QEvent* event) { + if (event->type() == QEvent::Wheel) { + float angle = static_cast(event)->angleDelta().y(); + const float ANGLE_SCALE = 1.0f / 1000.0f; + _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); + return true; + + } else if (event->type() == QEvent::MouseButtonPress) { + applyValue(_position, _radius->value()); + return true; + } + return false; +} + +VoxelColorSphereTool::VoxelColorSphereTool(MetavoxelEditor* editor) : + SphereTool(editor, "Set Voxel Color (Sphere)") { + + _form->addRow("Color:", _color = new QColorEditor(this)); +} + +bool VoxelColorSphereTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("VoxelColorAttribute"); +} + +QColor VoxelColorSphereTool::getColor() { + return _color->getColor(); +} + +void VoxelColorSphereTool::applyValue(const glm::vec3& position, float radius) { + MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorSphereEdit(position, radius, + _editor->getGridSpacing(), _color->getColor())) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); +} + +VoxelMaterialSphereTool::VoxelMaterialSphereTool(MetavoxelEditor* editor) : + SphereTool(editor, "Set Voxel Material (Sphere)") { + + _form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false)); + connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialSphereTool::updateTexture); +} + +bool VoxelMaterialSphereTool::appliesTo(const AttributePointer& attribute) const { + return attribute->inherits("VoxelColorAttribute"); +} + +QColor VoxelMaterialSphereTool::getColor() { + return _texture ? _texture->getAverageColor() : QColor(); +} + +void VoxelMaterialSphereTool::applyValue(const glm::vec3& position, float radius) { + SharedObjectPointer material = _materialEditor->getObject(); + _materialEditor->detachObject(); + MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSphereEdit(position, radius, + _editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); +} + +void VoxelMaterialSphereTool::updateTexture() { + MaterialObject* material = static_cast(_materialEditor->getObject().data()); + _texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE); +} diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index ba455dd96c..96f4a2abe9 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -455,4 +455,75 @@ private: QSharedPointer _texture; }; +/// Base class for tools based on a sphere brush. +class SphereTool : public MetavoxelTool { + Q_OBJECT + +public: + + SphereTool(MetavoxelEditor* editor, const QString& name); + + virtual void render(); + + virtual bool eventFilter(QObject* watched, QEvent* event); + +protected: + + virtual QColor getColor() = 0; + + virtual void applyValue(const glm::vec3& position, float radius) = 0; + + QFormLayout* _form; + QDoubleSpinBox* _radius; + + glm::vec3 _position; +}; + +/// Allows setting voxel colors by moving a sphere around. +class VoxelColorSphereTool : public SphereTool { + Q_OBJECT + +public: + + VoxelColorSphereTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QColor getColor(); + + virtual void applyValue(const glm::vec3& position, float radius); + +private: + + QColorEditor* _color; +}; + +/// Allows setting voxel materials by moving a sphere around. +class VoxelMaterialSphereTool : public SphereTool { + Q_OBJECT + +public: + + VoxelMaterialSphereTool(MetavoxelEditor* editor); + + virtual bool appliesTo(const AttributePointer& attribute) const; + +protected: + + virtual QColor getColor(); + + virtual void applyValue(const glm::vec3& position, float radius); + +private slots: + + void updateTexture(); + +private: + + SharedObjectEditor* _materialEditor; + QSharedPointer _texture; +}; + #endif // hifi_MetavoxelEditor_h diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index db684bf1c5..70b9a1e343 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -406,6 +406,10 @@ QRgb packNormal(const glm::vec3& normal) { return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE)); } +QRgb packNormal(const glm::vec3& normal, int alpha) { + return qRgba((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE), alpha); +} + glm::vec3 unpackNormal(QRgb value) { return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE, (char)qBlue(value) * INVERSE_CHAR_SCALE); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 371bb24852..cee01cdbef 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -416,6 +416,9 @@ public: /// Packs a normal into an RGB value. QRgb packNormal(const glm::vec3& normal); +/// Packs a normal (plus extra alpha value) into an RGBA value. +QRgb packNormal(const glm::vec3& normal, int alpha); + /// Unpacks a normal from an RGB value. glm::vec3 unpackNormal(QRgb value); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index da886cbf5d..178f6f7523 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -814,3 +814,267 @@ void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor); data.guide(visitor); } + +VoxelColorSphereEdit::VoxelColorSphereEdit(const glm::vec3& center, float radius, float granularity, const QColor& color) : + center(center), + radius(radius), + granularity(granularity), + color(color) { +} + +class VoxelMaterialSphereEditVisitor : public MetavoxelVisitor { +public: + + VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds, float granularity, + const SharedObjectPointer& material, const QColor& color); + + virtual int visit(MetavoxelInfo& info); + +private: + + glm::vec3 _center; + float _radius; + Box _bounds; + float _granularity; + SharedObjectPointer _material; + QColor _color; + float _blockSize; +}; + +VoxelMaterialSphereEditVisitor::VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds, + float granularity, const SharedObjectPointer& material, const QColor& color) : + MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector() << + AttributeRegistry::getInstance()->getVoxelColorAttribute() << + AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << + AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), + _center(center), + _radius(radius), + _bounds(bounds), + _granularity(granularity), + _material(material), + _color(color), + _blockSize(granularity * VOXEL_BLOCK_SIZE) { +} + +int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) { + Box bounds = info.getBounds(); + if (!bounds.intersects(_bounds)) { + return STOP_RECURSION; + } + if (info.size > _blockSize) { + return DEFAULT_ORDER; + } + VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); + QVector colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + colorPointer->getContents() : QVector(VOXEL_BLOCK_VOLUME); + + Box overlap = info.getBounds().getIntersection(_bounds); + float scale = VOXEL_BLOCK_SIZE / info.size; + overlap.minimum = (overlap.minimum - info.minimum) * scale; + overlap.maximum = (overlap.maximum - info.minimum) * scale; + int minX = glm::ceil(overlap.minimum.x); + int minY = glm::ceil(overlap.minimum.y); + int minZ = glm::ceil(overlap.minimum.z); + int sizeX = (int)overlap.maximum.x - minX + 1; + int sizeY = (int)overlap.maximum.y - minY + 1; + int sizeZ = (int)overlap.maximum.z - minZ + 1; + + glm::vec3 relativeCenter = (_center - info.minimum) * scale; + float relativeRadius = _radius * scale; + float relativeRadiusSquared = relativeRadius * relativeRadius; + + QRgb rgb = _color.rgba(); + glm::vec3 position(0.0f, 0.0f, minZ); + for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) { + position.y = minY; + for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y++) { + position.x = minX; + for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) { + if (glm::distance(relativeCenter, position) <= relativeRadius) { + *destX = rgb; + } + } + } + } + + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), + encodeInline(newColorPointer)); + + VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue(); + QVector hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + hermitePointer->getContents() : QVector(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT); + int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT; + int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT; + + int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ; + int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ; + if (minX > 0) { + hermiteMinX--; + hermiteSizeX++; + } + if (minY > 0) { + hermiteMinY--; + hermiteSizeY++; + } + if (minZ > 0) { + hermiteMinZ--; + hermiteSizeZ++; + } + QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples + + hermiteMinX * VoxelHermiteData::EDGE_COUNT; + for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) { + QRgb* hermiteDestY = hermiteDestZ; + for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) { + QRgb* hermiteDestX = hermiteDestY; + for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, + hermiteDestX += VoxelHermiteData::EDGE_COUNT) { + hermiteDestX[0] = 0x0; + glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z); + if (x != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[1])) { + float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z; + float parameter = 0.5f; + if (radicand >= 0.0f) { + float root = glm::sqrt(radicand); + parameter = -offset.x - root; + if (parameter < 0.0f || parameter > 1.0f) { + parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f); + } + } + glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + float length = glm::length(normal); + if (length > EPSILON) { + normal /= length; + } else { + normal = glm::vec3(0.0f, 1.0f, 0.0f); + } + hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + } + } + hermiteDestX[1] = 0x0; + if (y != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { + float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z; + float parameter = 0.5f; + if (radicand >= 0.0f) { + float root = glm::sqrt(radicand); + parameter = -offset.y - root; + if (parameter < 0.0f || parameter > 1.0f) { + parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f); + } + } + glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + float length = glm::length(normal); + if (length > EPSILON) { + normal /= length; + } else { + normal = glm::vec3(1.0f, 0.0f, 0.0f); + } + hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + } + } + hermiteDestX[2] = 0x0; + if (z != VOXEL_BLOCK_SIZE) { + const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int alpha0 = qAlpha(color[0]); + if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) { + float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y; + float parameter = 0.5f; + if (radicand >= 0.0f) { + float root = glm::sqrt(radicand); + parameter = -offset.z - root; + if (parameter < 0.0f || parameter > 1.0f) { + parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f); + } + } + glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + float length = glm::length(normal); + if (length > EPSILON) { + normal /= length; + } else { + normal = glm::vec3(1.0f, 0.0f, 0.0f); + } + hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + } + } + } + } + } + + VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES)); + info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), + encodeInline(newHermitePointer)); + + VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); + QByteArray materialContents; + QVector materials; + if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) { + materialContents = materialPointer->getContents(); + materials = materialPointer->getMaterials(); + + } else { + materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0); + } + + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); + position.z = minZ; + for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) { + position.y = minY; + for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y++) { + position.x = minX; + for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) { + if (glm::distance(relativeCenter, position) <= relativeRadius) { + *destX = materialIndex; + } + } + } + } + + clearUnusedMaterials(materials, materialContents); + VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials)); + info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline(newMaterialPointer)); + + return STOP_RECURSION; +} + +void VoxelColorSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // expand to fit the entire edit + glm::vec3 extents(radius, radius, radius); + Box bounds(center - extents, center + extents); + while (!data.getBounds().contains(bounds)) { + data.expand(); + } + VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, SharedObjectPointer(), color); + data.guide(visitor); +} + +VoxelMaterialSphereEdit::VoxelMaterialSphereEdit(const glm::vec3& center, float radius, float granularity, + const SharedObjectPointer& material, const QColor& averageColor) : + center(center), + radius(radius), + granularity(granularity), + material(material), + averageColor(averageColor) { +} + +void VoxelMaterialSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const { + // expand to fit the entire edit + glm::vec3 extents(radius, radius, radius); + Box bounds(center - extents, center + extents); + while (!data.getBounds().contains(bounds)) { + data.expand(); + } + VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, material, averageColor); + data.guide(visitor); +} diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 15ab0d92cb..2eb5684d77 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -296,4 +296,43 @@ public: DECLARE_STREAMABLE_METATYPE(VoxelMaterialBoxEdit) +/// An edit that sets the color of voxels within a sphere to a value. +class VoxelColorSphereEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 center; + STREAM float radius; + STREAM float granularity; + STREAM QColor color; + + VoxelColorSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f, + float granularity = 0.0f, const QColor& color = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(VoxelColorSphereEdit) + +/// An edit that sets the materials of voxels within a sphere to a value. +class VoxelMaterialSphereEdit : public MetavoxelEdit { + STREAMABLE + +public: + + STREAM glm::vec3 center; + STREAM float radius; + STREAM float granularity; + STREAM SharedObjectPointer material; + STREAM QColor averageColor; + + VoxelMaterialSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f, float granularity = 0.0f, + const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor()); + + virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const; +}; + +DECLARE_STREAMABLE_METATYPE(VoxelMaterialSphereEdit) + #endif // hifi_MetavoxelMessages_h From 0cc6f559d4dfe7d303e9adfa5d03849d77bc260d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:13:54 -0700 Subject: [PATCH 19/51] add AACube::operator<() so they can be sorted --- libraries/shared/src/AACube.cpp | 7 +++++++ libraries/shared/src/AACube.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/libraries/shared/src/AACube.cpp b/libraries/shared/src/AACube.cpp index e359eac9e9..6db80026df 100644 --- a/libraries/shared/src/AACube.cpp +++ b/libraries/shared/src/AACube.cpp @@ -314,6 +314,13 @@ bool AACube::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end return true; } +bool AACube::operator<(const AACube& otherCube) const { + return (_corner.x < otherCube._corner.x + || (_corner.x == otherCube._corner.x && (_corner.y < otherCube._corner.y + || (_corner.y == otherCube._corner.y && (_corner.z < otherCube._corner.z + || _scale < otherCube._scale))))); +} + glm::vec3 AACube::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const { switch (face) { case MIN_X_FACE: diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index b427e579e9..5e2595811c 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -50,6 +50,8 @@ public: bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const; bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const; + bool operator<(const AACube& otherCube) const; // for qSorted lists of AACubes + private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; From 06e1d4a8bc02a32defc48b2f8ef2fff104a09e7c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:16:49 -0700 Subject: [PATCH 20/51] init Shape::_mass in all ctors --- libraries/shared/src/Shape.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 52369139c0..345d69d8e4 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -81,17 +81,22 @@ public: protected: // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() { + Shape(Type type) : _type(type), _owningEntity(NULL), + _boundingRadius(0.f), _translation(0.f), + _rotation(), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } - Shape(Type type, const glm::vec3& position) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation() { + Shape(Type type, const glm::vec3& position) : + _type(type), _owningEntity(NULL), + _boundingRadius(0.f), _translation(position), + _rotation(), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } - Shape(Type type, const glm::vec3& position, const glm::quat& rotation) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation(rotation) { + Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL), + _boundingRadius(0.f), _translation(position), + _rotation(rotation), _mass(MAX_SHAPE_MASS) { _id = getNextID(); } From 54463ab6687b1585ed071b0e04b6b5e4c0124267 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:17:47 -0700 Subject: [PATCH 21/51] add findContentInCube() and typedef CubeList --- libraries/octree/src/Octree.cpp | 36 +++++++++++++++++++++++++++++++++ libraries/octree/src/Octree.h | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index c11d23c2ec..2cf90fcb90 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -19,6 +19,7 @@ #include // to load voxels from file #include +#include #include #include @@ -730,6 +731,12 @@ public: bool found; }; +class ContentArgs { +public: + AACube cube; + CubeList* cubes; +}; + bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) { CapsuleArgs* args = static_cast(extraData); @@ -772,6 +779,25 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { return false; } +bool findContentInCubeOp(OctreeElement* element, void* extraData) { + ContentArgs* args = static_cast(extraData); + + // coarse check against bounds + AACube cube = element->getAACube(); + cube.scale(TREE_SCALE); + if (!cube.touches(args->cube)) { + return false; + } + if (!element->isLeaf()) { + return true; // recurse on children + } + if (element->hasContent()) { + args->cubes->push_back(cube); + return true; + } + return false; +} + bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) { @@ -840,6 +866,16 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, return args.found; } +bool Octree::findContentInCube(const AACube& cube, CubeList& cubes) { + if (!tryLockForRead()) { + return false; + } + ContentArgs args = { cube, &cubes }; + recurseTreeWithOperation(findContentInCubeOp, &args); + unlock(); + return true; +} + class GetElementEnclosingArgs { public: OctreeElement* element; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 7ab22598ef..dc11bb8d1c 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -35,6 +35,7 @@ class Shape; #include #include +#include /// derive from this class to use the Octree::recurseTreeWithOperator() method class RecurseOctreeOperator { @@ -46,6 +47,7 @@ public: // Callback function, for recuseTreeWithOperation typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; +typedef QVector CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true; @@ -280,6 +282,8 @@ public: bool findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType = Octree::TryLock, bool* accurateResult = NULL); + bool findContentInCube(const AACube& cube, CubeList& cubes); + OctreeElement* getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); From 6454fd26277c0754cbe24928aae528b8f5fdf42f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:18:12 -0700 Subject: [PATCH 22/51] add PhysicsEntity::stepForward() --- libraries/shared/src/PhysicsEntity.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index 3407ac8421..9f98cc96ca 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -33,6 +33,8 @@ public: PhysicsEntity(); virtual ~PhysicsEntity(); + virtual void stepForward(float deltaTime) { } + void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); From 361d1e83b0e49e777e54513a20f5083754dd6a22 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:18:35 -0700 Subject: [PATCH 23/51] use PhysicsEntity::stepForward() in simulation loop --- libraries/shared/src/PhysicsSimulation.cpp | 27 ++++++++++++++++++++-- libraries/shared/src/PhysicsSimulation.h | 5 +++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index b58f62dfd4..93f0797c94 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -30,6 +30,10 @@ PhysicsSimulation::PhysicsSimulation() : _translation(0.0f), _frameCount(0), _en } PhysicsSimulation::~PhysicsSimulation() { + clear(); +} + +void PhysicsSimulation::clear() { // entities have a backpointer to this simulator that must be cleaned up int numEntities = _otherEntities.size(); for (int i = 0; i < numEntities; ++i) { @@ -43,6 +47,9 @@ PhysicsSimulation::~PhysicsSimulation() { // but Ragdolls do not _ragdoll = NULL; _otherRagdolls.clear(); + + // contacts have backpointers to shapes so we clear them + _contacts.clear(); } void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) { @@ -134,6 +141,18 @@ void PhysicsSimulation::removeShapes(const PhysicsEntity* entity) { } } +void PhysicsSimulation::removeShape(const Shape* shape) { + // remove data structures with pointers to shape + QMap::iterator itr = _contacts.begin(); + while (itr != _contacts.end()) { + if (shape == itr.value().getShapeA() || shape == itr.value().getShapeB()) { + itr = _contacts.erase(itr); + } else { + ++itr; + } + } +} + const float OTHER_RAGDOLL_MASS_SCALE = 10.0f; bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { @@ -195,7 +214,7 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter quint64 startTime = now; quint64 expiry = startTime + maxUsec; - moveRagdolls(deltaTime); + integrate(deltaTime); enforceContacts(); int numDolls = _otherRagdolls.size(); { @@ -238,8 +257,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter pruneContacts(); } -void PhysicsSimulation::moveRagdolls(float deltaTime) { +void PhysicsSimulation::integrate(float deltaTime) { PerformanceTimer perfTimer("integrate"); + int numEntities = _otherEntities.size(); + for (int i = 0; i < numEntities; ++i) { + _otherEntities[i]->stepForward(deltaTime); + } _ragdoll->stepForward(deltaTime); int numDolls = _otherRagdolls.size(); for (int i = 0; i < numDolls; ++i) { diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/shared/src/PhysicsSimulation.h index 1db56a46e2..955029c174 100644 --- a/libraries/shared/src/PhysicsSimulation.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -27,6 +27,8 @@ public: PhysicsSimulation(); ~PhysicsSimulation(); + + void clear(); void setTranslation(const glm::vec3& translation) { _translation = translation; } const glm::vec3& getTranslation() const { return _translation; } @@ -39,6 +41,7 @@ public: void removeEntity(PhysicsEntity* entity); void removeShapes(const PhysicsEntity* entity); + void removeShape(const Shape* shape); /// \return true if doll was added to or is already in the list bool addRagdoll(Ragdoll* doll); @@ -52,7 +55,7 @@ public: void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec); protected: - void moveRagdolls(float deltaTime); + void integrate(float deltaTime); /// \return true if main ragdoll collides with other avatar bool computeCollisions(); From 83e04a0e8ffad1a30a22232fba7cfed39637750e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:19:14 -0700 Subject: [PATCH 24/51] add VoxelShapeManager class --- interface/src/avatar/VoxelShapeManager.cpp | 131 +++++++++++++++++++++ interface/src/avatar/VoxelShapeManager.h | 48 ++++++++ 2 files changed, 179 insertions(+) create mode 100644 interface/src/avatar/VoxelShapeManager.cpp create mode 100644 interface/src/avatar/VoxelShapeManager.h diff --git a/interface/src/avatar/VoxelShapeManager.cpp b/interface/src/avatar/VoxelShapeManager.cpp new file mode 100644 index 0000000000..782fa3fa71 --- /dev/null +++ b/interface/src/avatar/VoxelShapeManager.cpp @@ -0,0 +1,131 @@ +// +// VoxelShapeManager.cpp +// interface/src/avatar +// +// Created by Andrew Meadows on 2014.09.02 +// Copyright 2012 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include +#include +#include + +#include "VoxelShapeManager.h" + +VoxelShapeManager::VoxelShapeManager() : PhysicsEntity(), _lastSimulationTranslation(0.0f) { +} + +VoxelShapeManager::~VoxelShapeManager() { + clearShapes(); +} + +void VoxelShapeManager::stepForward(float deltaTime) { + PhysicsSimulation* simulation = getSimulation(); + if (simulation) { + glm::vec3 simulationOrigin = simulation->getTranslation(); + if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) { + int numVoxels = _voxels.size(); + for (int i = 0; i < numVoxels; ++i) { + // the shape's position is stored in the simulation-frame + glm::vec3 cubeCenter = _voxels[i]._cube.calcCenter(); + _voxels[i]._shape->setTranslation(cubeCenter - simulationOrigin); + } + _lastSimulationTranslation = simulationOrigin; + } + } +} + +void VoxelShapeManager::buildShapes() { + // the shapes are owned by the elements of _voxels, + // so _shapes is constructed by harvesting them from _voxels + _shapes.clear(); + int numVoxels = _voxels.size(); + for (int i = 0; i < numVoxels; ++i) { + _shapes.push_back(_voxels[i]._shape); + } +} + +void VoxelShapeManager::clearShapes() { + PhysicsEntity::clearShapes(); + _voxels.clear(); +} + +void VoxelShapeManager::updateVoxels(CubeList& cubes) { + PhysicsSimulation* simulation = getSimulation(); + if (!simulation) { + return; + } + // sort incoming cubes + qSort(cubes); + + // Some of the cubes are new, others already exist, and some old voxels no longer exist. + // Since both lists are sorted we walk them simultaneously looking for matches. + QVector cubesToAdd; + QVector voxelsToRemove; + int numCubes = cubes.size(); + int numVoxels = _voxels.size(); + int j = 0; + for (int i = 0; i < numCubes; ++i) { + while (j < numVoxels && _voxels[j]._cube < cubes[i]) { + // remove non-matching voxels not found in cubes + voxelsToRemove.push_back(j++); + } + if (j < numVoxels) { + if (glm::distance2(cubes[i].getCorner(), _voxels[j]._cube.getCorner()) < EPSILON) { + if (cubes[i].getScale() != _voxels[j]._cube.getScale()) { + // the voxel changed scale so we replace + voxelsToRemove.push_back(j++); + cubesToAdd.push_back(i); + } else { + // the voxel already exists + ++j; + } + } else { + // the voxel doesn't exist yet + cubesToAdd.push_back(i); + } + } else { + // all existing voxels have already been processed, so this one is new + cubesToAdd.push_back(i); + } + } + while (j < numVoxels) { + // remove non-matching voxels at the end + voxelsToRemove.push_back(j++); + } + + // remove voxels identified as old, from back to front + for (int i = voxelsToRemove.size() - 1; i >= 0; --i) { + int k = voxelsToRemove[i]; + simulation->removeShape(_voxels[k]._shape); + if (k < numVoxels - 1) { + // copy the last voxel into this spot + _voxels[k] = _voxels[numVoxels - 1]; + } + _voxels.pop_back(); + --numVoxels; + } + + // add new voxels + glm::vec3 simulationOrigin = simulation->getTranslation(); + for (int i = 0; i < cubesToAdd.size(); ++i) { + const AACube& cube = cubes[cubesToAdd[i]]; + // NOTE: shape's position is in simulation frame + AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin); + shape->setEntity(this); + VoxelInfo voxel = { cube, shape }; + _voxels.push_back(voxel); + ++numVoxels; + } + + if (cubesToAdd.size() > 0 || voxelsToRemove.size() > 0) { + // keep _voxels sorted + qSort(_voxels); + buildShapes(); + } +} diff --git a/interface/src/avatar/VoxelShapeManager.h b/interface/src/avatar/VoxelShapeManager.h new file mode 100644 index 0000000000..d5642801b9 --- /dev/null +++ b/interface/src/avatar/VoxelShapeManager.h @@ -0,0 +1,48 @@ +// +// VoxelShapeManager.h +// interface/src/avatar +// +// Created by Andrew Meadows on 2014.09.02 +// Copyright 2012 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_VoxelShapeManager_h +#define hifi_VoxelShapeManager_h + +#include +#include +#include + +#include "VoxelShapeManager.h" + +class AACubeShape; + +class VoxelInfo{ +public: + bool operator<(const VoxelInfo& otherVoxel) const { return _cube < otherVoxel._cube; } + AACube _cube; + AACubeShape* _shape; +}; + +class VoxelShapeManager : public PhysicsEntity { +public: + VoxelShapeManager(); + ~VoxelShapeManager(); + + void stepForward(float deltaTime); + void buildShapes(); + void clearShapes(); + + /// \param cubes list of AACubes representing all of the voxels that should be in this VoxelShapeManager + void updateVoxels(CubeList& cubes); + + +private: + glm::vec3 _lastSimulationTranslation; + QVector _voxels; +}; + +#endif // hifi_VoxelShapeManager_h From 2c1aa31d1e80bbefac119866f8354d71afa6cbbb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 13:19:41 -0700 Subject: [PATCH 25/51] give MyAvatar a VoxelShapeManager member --- interface/src/avatar/MyAvatar.cpp | 217 ++++++++++++++++-------------- interface/src/avatar/MyAvatar.h | 2 + 2 files changed, 119 insertions(+), 100 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e51390f9d0..cf25eab00d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -79,7 +79,8 @@ MyAvatar::MyAvatar() : _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), - _physicsSimulation() + _physicsSimulation(), + _voxelShapeManager() { ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { @@ -90,11 +91,11 @@ MyAvatar::MyAvatar() : _skeletonModel.setEnableShapes(true); Ragdoll* ragdoll = _skeletonModel.buildRagdoll(); _physicsSimulation.setRagdoll(ragdoll); + _physicsSimulation.addEntity(&_voxelShapeManager); } MyAvatar::~MyAvatar() { - _physicsSimulation.setRagdoll(NULL); - _physicsSimulation.setEntity(NULL); + _physicsSimulation.clear(); _lookAtTargetAvatar.clear(); } @@ -1486,112 +1487,125 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { static CollisionList myCollisions(64); void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { - const float MAX_VOXEL_COLLISION_SPEED = 100.0f; - float speed = glm::length(_velocity); - if (speed > MAX_VOXEL_COLLISION_SPEED) { - // don't even bother to try to collide against voxles when moving very fast - _trapDuration = 0.0f; - return; - } - bool isTrapped = false; - myCollisions.clear(); - const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); - if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) { - const float VOXEL_ELASTICITY = 0.0f; - const float VOXEL_DAMPING = 0.0f; - float capsuleRadius = boundingShape.getRadius(); - float capsuleHalfHeight = boundingShape.getHalfHeight(); - const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; - const float MIN_STEP_HEIGHT = 0.0f; - glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; - float highestStep = 0.0f; - float lowestStep = MAX_STEP_HEIGHT; - glm::vec3 floorPoint; - glm::vec3 stepPenetration(0.0f); - glm::vec3 totalPenetration(0.0f); + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + // compute the bounding cube around avatar + float radius = 2.0f * getBoundingRadius(); + glm::vec3 corner = getPosition() - glm::vec3(radius); + AACube boundingCube(corner, 2.0f * radius); - for (int i = 0; i < myCollisions.size(); ++i) { - CollisionInfo* collision = myCollisions[i]; - glm::vec3 cubeCenter = collision->_vecData; - float cubeSide = collision->_floatData; - float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection); - float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection); - const float MAX_TRAP_PERIOD = 0.125f; - if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { - isTrapped = true; - if (_trapDuration > MAX_TRAP_PERIOD) { - float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection); - if (distance < 0.0f) { - distance = fabsf(distance) + 0.5f * cubeSide; - } - distance += capsuleRadius + capsuleHalfHeight; - totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection); - continue; - } - } else if (_trapDuration > MAX_TRAP_PERIOD) { - // we're trapped, ignore this collision - continue; - } - totalPenetration = addPenetrations(totalPenetration, collision->_penetration); - if (glm::dot(collision->_penetration, _velocity) >= 0.0f) { - glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection; - float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase); - if (stepHeight > highestStep) { - highestStep = stepHeight; - stepPenetration = collision->_penetration; - } - if (stepHeight < lowestStep) { - lowestStep = stepHeight; - floorPoint = collision->_contactPoint - collision->_penetration; - } - } + // query the VoxelTree for cubes that touch avatar's boundingCube + CubeList cubes; + if (Application::getInstance()->getVoxelTree()->findContentInCube(boundingCube, cubes)) { + _voxelShapeManager.updateVoxels(cubes); } - if (lowestStep < MAX_STEP_HEIGHT) { - _lastFloorContactPoint = floorPoint; - } - - float penetrationLength = glm::length(totalPenetration); - if (penetrationLength < EPSILON) { + } else { + const float MAX_VOXEL_COLLISION_SPEED = 100.0f; + float speed = glm::length(_velocity); + if (speed > MAX_VOXEL_COLLISION_SPEED) { + // don't even bother to try to collide against voxles when moving very fast _trapDuration = 0.0f; return; } - float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection); - if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) { - // we're colliding against an edge - glm::vec3 targetVelocity = _motorVelocity; - if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { - // rotate _motorVelocity into world frame - glm::quat rotation = getHead()->getCameraOrientation(); - targetVelocity = rotation * _motorVelocity; - } - if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) { - // we're puhing into the edge, so we want to lift - - // remove unhelpful horizontal component of the step's penetration - totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection); - - // further adjust penetration to help lift - float liftSpeed = glm::max(MAX_WALKING_SPEED, speed); - float thisStep = glm::min(liftSpeed * deltaTime, highestStep); - float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep; - if (extraStep > 0.0f) { - totalPenetration -= extraStep * _worldUpDirection; + bool isTrapped = false; + myCollisions.clear(); + const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); + if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) { + const float VOXEL_ELASTICITY = 0.0f; + const float VOXEL_DAMPING = 0.0f; + float capsuleRadius = boundingShape.getRadius(); + float capsuleHalfHeight = boundingShape.getHalfHeight(); + const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; + const float MIN_STEP_HEIGHT = 0.0f; + glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; + float highestStep = 0.0f; + float lowestStep = MAX_STEP_HEIGHT; + glm::vec3 floorPoint; + glm::vec3 stepPenetration(0.0f); + glm::vec3 totalPenetration(0.0f); + + for (int i = 0; i < myCollisions.size(); ++i) { + CollisionInfo* collision = myCollisions[i]; + glm::vec3 cubeCenter = collision->_vecData; + float cubeSide = collision->_floatData; + float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection); + float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection); + const float MAX_TRAP_PERIOD = 0.125f; + if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { + isTrapped = true; + if (_trapDuration > MAX_TRAP_PERIOD) { + float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection); + if (distance < 0.0f) { + distance = fabsf(distance) + 0.5f * cubeSide; + } + distance += capsuleRadius + capsuleHalfHeight; + totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection); + continue; + } + } else if (_trapDuration > MAX_TRAP_PERIOD) { + // we're trapped, ignore this collision + continue; + } + totalPenetration = addPenetrations(totalPenetration, collision->_penetration); + if (glm::dot(collision->_penetration, _velocity) >= 0.0f) { + glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection; + float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase); + if (stepHeight > highestStep) { + highestStep = stepHeight; + stepPenetration = collision->_penetration; + } + if (stepHeight < lowestStep) { + lowestStep = stepHeight; + floorPoint = collision->_contactPoint - collision->_penetration; + } + } + } + if (lowestStep < MAX_STEP_HEIGHT) { + _lastFloorContactPoint = floorPoint; + } + + float penetrationLength = glm::length(totalPenetration); + if (penetrationLength < EPSILON) { + _trapDuration = 0.0f; + return; + } + float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection); + if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) { + // we're colliding against an edge + glm::vec3 targetVelocity = _motorVelocity; + if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { + // rotate _motorVelocity into world frame + glm::quat rotation = getHead()->getCameraOrientation(); + targetVelocity = rotation * _motorVelocity; + } + if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) { + // we're puhing into the edge, so we want to lift + + // remove unhelpful horizontal component of the step's penetration + totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection); + + // further adjust penetration to help lift + float liftSpeed = glm::max(MAX_WALKING_SPEED, speed); + float thisStep = glm::min(liftSpeed * deltaTime, highestStep); + float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep; + if (extraStep > 0.0f) { + totalPenetration -= extraStep * _worldUpDirection; + } + + _position -= totalPenetration; + } else { + // we're not pushing into the edge, so let the avatar fall + applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); } - - _position -= totalPenetration; } else { - // we're not pushing into the edge, so let the avatar fall applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); } - } else { - applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING); - } - - // Don't make a collision sound against voxlels by default -- too annoying when walking - //const float VOXEL_COLLISION_FREQUENCY = 0.5f; - //updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); - } - _trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f; + + // Don't make a collision sound against voxlels by default -- too annoying when walking + //const float VOXEL_COLLISION_FREQUENCY = 0.5f; + //updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY); + } + _trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f; + } } void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) { @@ -1952,6 +1966,9 @@ void MyAvatar::updateMotionBehaviorsFromMenu() { } else { _motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; } + if (!(_collisionGroups | COLLISION_GROUP_VOXELS)) { + _voxelShapeManager.clearShapes(); + } } void MyAvatar::renderAttachments(RenderMode renderMode) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3d79d79a82..e874f36b0d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -17,6 +17,7 @@ #include #include "Avatar.h" +#include "VoxelShapeManager.h" class ModelItemID; @@ -214,6 +215,7 @@ private: QList _animationHandles; PhysicsSimulation _physicsSimulation; + VoxelShapeManager _voxelShapeManager; RecorderPointer _recorder; From 0bd800faf2c0efe858cae54a5c04716a1a1f1e89 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Sep 2014 14:09:04 -0700 Subject: [PATCH 26/51] Starting on materials attributes, fixed bug with Hermite deltas. --- interface/src/MetavoxelSystem.cpp | 27 ++++++++++++++++++- interface/src/MetavoxelSystem.h | 2 ++ .../metavoxels/src/AttributeRegistry.cpp | 4 +-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 4b1bfa56ac..8de78cc5be 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1828,6 +1828,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } glm::vec3 center; glm::vec3 normal; + const int MAX_MATERIALS_PER_VERTEX = 4; + quint8 materials[4]; + glm::vec4 materialWeights; + float totalWeight = 0.0f; int red = 0, green = 0, blue = 0; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; @@ -1836,13 +1840,34 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); + if (crossing.material != 0) { + for (int j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { + if (materials[j] == crossing.material) { + materialWeights[j] += 1.0f; + totalWeight += 1.0f; + break; + + } else if (materials[j] == 0.0f) { + materials[j] = crossing.material; + materialWeights[j] = 1.0f; + totalWeight += 1.0f; + break; + } + } + } } normal = glm::normalize(normal); center /= crossingCount; + if (totalWeight > 0.0f) { + materialWeights *= (EIGHT_BIT_MAXIMUM / totalWeight); + } VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center * EIGHT_BIT_MAXIMUM_RECIPROCAL) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, - { (char)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) } }; + { (char)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) }, + { materials[0], materials[1], materials[2], materials[3] }, + { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], + (quint8)materialWeights[3] } }; int index = vertices.size(); vertices.append(point); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 3be00543c2..aa15dcc8db 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -220,6 +220,8 @@ public: glm::vec3 vertex; quint8 color[3]; char normal[3]; + quint8 materials[4]; + quint8 materialWeights[4]; }; /// Contains the information necessary to render a voxel block. diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 70b9a1e343..aec9a069be 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1907,8 +1907,8 @@ void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& bool differenceZ = false; for (int y = 0; y < _size; y++) { bool differenceY = false; - for (int x = 0; x < _size; x++) { - if (*src++ != *ref++ || *src++ != *ref++ || *src++ != *ref++) { + for (int x = 0; x < _size; x++, src += EDGE_COUNT, ref += EDGE_COUNT) { + if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) { minX = qMin(minX, x); maxX = qMax(maxX, x); differenceY = differenceZ = true; From a02656070f33bc4dbb300a35b9c4e05ad1bde00e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Sep 2014 14:43:11 -0700 Subject: [PATCH 27/51] Added some comments. --- interface/src/MetavoxelSystem.cpp | 32 +++++++++++++++---- .../metavoxels/src/MetavoxelMessages.cpp | 3 ++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 8de78cc5be..f135579526 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1570,11 +1570,17 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { QVector vertices; QVector indices; + // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the + // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges const QVector& colorContents = color->getContents(); const QByteArray& materialContents = material->getContents(); const QVector& hermiteContents = hermite->getContents(); int size = color->getSize(); int area = size * size; + + // number variables such as offset3 and alpha0 in this function correspond to cube corners, where the x, y, and z + // components are represented as bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at + // the minimum x, y, and z corner and alpha7 is the value at the maximum x, y, and z int offset3 = size + 1; int offset5 = area + 1; int offset6 = area + size; @@ -1587,6 +1593,8 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { const char* materialData = materialContents.constData(); + // as we scan down the cube generating vertices between grid points, we remember the indices of the last + // (element, line, section--x, y, z) so that we can connect generated vertices as quads int expanded = size + 1; QVector lineIndices(expanded, -1); QVector lastLineIndices(expanded, -1); @@ -1609,6 +1617,9 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; int alphaTotal = alpha0; int possibleTotal = EIGHT_BIT_MAXIMUM; + + // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between + // neighboring blocks, which share only one layer of points bool middleX = (x != 0 && x != size); bool middleY = (y != 0 && y != size); bool middleZ = (z != 0 && z != size); @@ -1653,6 +1664,8 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } continue; // no corners set/all corners set } + // the terrifying conditional code that follows checks each cube edge for a crossing, gathering + // its properties (color, material, normal) if one is present; as before, boundary edges are excluded int clampedX = qMax(x - 1, 0), clampedY = qMax(y - 1, 0), clampedZ = qMax(z - 1, 0); const QRgb* hermiteBase = hermiteData + clampedZ * hermiteArea + clampedY * hermiteStride + clampedX * VoxelHermiteData::EDGE_COUNT; @@ -1826,6 +1839,8 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { } crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite)); } + // at present, we simply average the properties of each crossing as opposed to finding the vertex that + // minimizes the quadratic error function as described in the reference paper glm::vec3 center; glm::vec3 normal; const int MAX_MATERIALS_PER_VERTEX = 4; @@ -1840,6 +1855,9 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); + + // when assigning a material, search for its presence and, if not found, + // place it in the first empty slot if (crossing.material != 0) { for (int j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { if (materials[j] == crossing.material) { @@ -1871,17 +1889,19 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index = vertices.size(); vertices.append(point); + // the first x, y, and z are repeated for the boundary edge; past that, we consider generating + // quads for each edge that includes a transition, using indices of previously generated vertices if (x != 0 && y != 0 && z != 0) { if (alpha0 != alpha1) { indices.append(index); int index1 = lastLineIndices.at(x); int index2 = lastPlaneIndices.at((y - 1) * expanded + x); int index3 = lastPlaneIndices.at(y * expanded + x); - if (alpha0 == 0) { + if (alpha0 == 0) { // quad faces negative x indices.append(index3); indices.append(index2); indices.append(index1); - } else { + } else { // quad faces positive x indices.append(index1); indices.append(index2); indices.append(index3); @@ -1893,11 +1913,11 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index1 = lastIndex; int index2 = lastPlaneIndices.at(y * expanded + x - 1); int index3 = lastPlaneIndices.at(y * expanded + x); - if (alpha0 == 0) { + if (alpha0 == 0) { // quad faces negative y indices.append(index1); indices.append(index2); indices.append(index3); - } else { + } else { // quad faces positive y indices.append(index3); indices.append(index2); indices.append(index1); @@ -1909,11 +1929,11 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { int index1 = lastIndex; int index2 = lastLineIndices.at(x - 1); int index3 = lastLineIndices.at(x); - if (alpha0 == 0) { + if (alpha0 == 0) { // quad faces negative z indices.append(index3); indices.append(index2); indices.append(index1); - } else { + } else { // quad faces positive z indices.append(index1); indices.append(index2); indices.append(index3); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 178f6f7523..fcf53c1106 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -726,6 +726,7 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { QRgb* hermiteDestX = hermiteDestY; for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, hermiteDestX += VoxelHermiteData::EDGE_COUNT) { + // internal edges are set to zero; border edges (when non-terminal) are set to the intersection values hermiteDestX[0] = 0x0; if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) { const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; @@ -933,6 +934,8 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) { QRgb* hermiteDestX = hermiteDestY; for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, hermiteDestX += VoxelHermiteData::EDGE_COUNT) { + // at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the + // crossing and normal values based on intersection with the sphere hermiteDestX[0] = 0x0; glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z); if (x != VOXEL_BLOCK_SIZE) { From a3b26582ba9189211d0b386e5ca81970906ca5c2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Sep 2014 15:39:26 -0700 Subject: [PATCH 28/51] fix for nan in sphereVsAACubeHelper() --- libraries/shared/src/ShapeCollider.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 399d6e3d42..259b7c9118 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -676,8 +676,7 @@ CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereR // sphereCenter is touching cube surface, so we can't use the difference between those two // points to compute the penetration direction. Instead we use the unitary components of // cubeContact. - direction = cubeContact / halfCubeSide; - glm::modf(BA, direction); + glm::modf(cubeContact / halfCubeSide, direction); lengthDirection = glm::length(direction); } else if (lengthDirection > sphereRadius) { collisions.deleteLastCollision(); From 18fdb340c1e9d40731817187a86c68c6945caeec Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 Sep 2014 20:30:15 -0700 Subject: [PATCH 29/51] extended toolBars.js --- examples/toolBars.js | 78 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/examples/toolBars.js b/examples/toolBars.js index 092ab5992e..dc17f02869 100644 --- a/examples/toolBars.js +++ b/examples/toolBars.js @@ -61,11 +61,10 @@ Overlay2D = function(properties, overlay) { // overlay is an optionnal variable } this.clicked = function(clickedOverlay) { - return (overlay == clickedOverlay ? true : false); + return overlay === clickedOverlay; } this.cleanup = function() { - print("Cleanup"); Overlays.deleteOverlay(overlay); } } @@ -112,9 +111,9 @@ Tool = function(properties, selectable, selected) { // selectable and selected a this.select(selected); this.baseClicked = this.clicked; - this.clicked = function(clickedOverlay) { + this.clicked = function(clickedOverlay, update) { if (this.baseClicked(clickedOverlay)) { - if (selectable) { + if (selectable && update) { this.toggle(); } return true; @@ -141,6 +140,7 @@ ToolBar = function(x, y, direction) { alpha: 1.0, visible: false }); + this.spacing = []; this.addTool = function(properties, selectable, selected) { if (direction == ToolBar.HORIZONTAL) { @@ -154,16 +154,56 @@ ToolBar = function(x, y, direction) { this.width = Math.max(properties.width, this.width); this.height += properties.height + ToolBar.SPACING; } + if (this.back != null) { Overlays.editOverlay(this.back, { - width: this.width + 2 * ToolBar.SPACING, - height: this.height + 2 * ToolBar.SPACING + width: this.width + + ((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING, + height: this.height + + ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, }); } this.tools.push(new Tool(properties, selectable, selected)); return ((this.tools.length) - 1); } + + this.addSpacing = function(size) { + if (direction == ToolBar.HORIZONTAL) { + this.width += size; + } else { + this.height += size; + } + this.spacing[this.tools.length] = size; + + return (this.tools.length); + } + + this.changeSpacing = function(size, id) { + if (this.spacing[id] === null) { + this.spacing[id] = 0; + } + var diff = size - this.spacing[id]; + this.spacing[id] = size; + + var dx = (direction == ToolBar.HORIZONTAL) ? diff : 0; + var dy = (direction == ToolBar.VERTICAL) ? diff : 0; + this.width += dx; + this.height += dy; + + for(i = id; i < this.tools.length; i++) { + this.tools[i].move(this.tools[i].x() + dx, + this.tools[i].y() + dy); + } + if (this.back != null) { + Overlays.editOverlay(this.back, { + width: this.width + + ((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING, + height: this.height + + ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, + }); + } + } this.removeLastTool = function() { this.tools.pop().cleanup(); @@ -209,18 +249,22 @@ ToolBar = function(x, y, direction) { this.tools[tool].setAlpha(alpha); } } - + this.setBack = function(color, alpha) { if (color == null) { Overlays.editOverlay(this.back, { - visible: false - }); + visible: false + }); } else { Overlays.editOverlay(this.back, { - visible: true, - backgroundColor: color, - alpha: alpha - }) + width: this.width + + ((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING, + height: this.height + + ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, + visible: true, + backgroundColor: color, + alpha: alpha + }); } } @@ -233,9 +277,13 @@ ToolBar = function(x, y, direction) { } } - this.clicked = function(clickedOverlay) { + this.clicked = function(clickedOverlay, update) { + if(typeof(update) === 'undefined') { + update = true; + } + for(var tool in this.tools) { - if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) { + if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay, update)) { return parseInt(tool); } } From 417393c22e686960b5122e75a2a9aae4629826b7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 Sep 2014 20:31:57 -0700 Subject: [PATCH 30/51] New Recorder UI --- examples/Recorder.js | 216 +++++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 80 deletions(-) diff --git a/examples/Recorder.js b/examples/Recorder.js index 7ecf0e2640..bb42e4b292 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -13,7 +13,6 @@ Script.include("toolBars.js"); var recordingFile = "recording.rec"; var playFromCurrentLocation = true; -var loop = true; var windowDimensions = Controller.getViewportDimensions(); var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/"; @@ -21,69 +20,92 @@ var ALPHA_ON = 1.0; var ALPHA_OFF = 0.7; var COLOR_ON = { red: 128, green: 0, blue: 0 }; var COLOR_OFF = { red: 128, green: 128, blue: 128 }; -Tool.IMAGE_WIDTH *= 0.7; -Tool.IMAGE_HEIGHT *= 0.7; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; var toolBar = null; var recordIcon; var playIcon; +var playLoopIcon; var saveIcon; var loadIcon; +var spacing; +var timerOffset; setupToolBar(); var timer = null; setupTimer(); +var watchStop = false; + function setupToolBar() { if (toolBar != null) { print("Multiple calls to Recorder.js:setupToolBar()"); return; } - + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); - toolBar.setBack(COLOR_OFF, ALPHA_OFF); - - recordIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "record.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false); - - playIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "play.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false, false); - - saveIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "save.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false, false); - - loadIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "load.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false, false); + + toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); + + recordIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-record.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON, + visible: true + }, true, MyAvatar.isRecording()); + + playIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "play-pause.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + var playLoopWidthFactor = 1.65; + playLoopIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "play-and-loop.svg", + subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + timerOffset = toolBar.width; + spacing = toolBar.addSpacing(0); + + saveIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-save.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + loadIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-upload.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying()) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); } function setupTimer() { timer = Overlays.addOverlay("text", { - font: { size: 20 }, + font: { size: 15 }, text: (0.00).toFixed(3), backgroundColor: COLOR_OFF, x: 0, y: 0, - width: 100, - height: 100, + width: 0, + height: 0, alpha: 1.0, visible: true }); @@ -92,7 +114,8 @@ function setupTimer() { function updateTimer() { var text = ""; if (MyAvatar.isRecording()) { - text = formatTime(MyAvatar.recorderElapsed()) + text = formatTime(MyAvatar.recorderElapsed()); + } else { text = formatTime(MyAvatar.playerElapsed()) + " / " + formatTime(MyAvatar.playerLength()); @@ -101,6 +124,7 @@ function updateTimer() { Overlays.editOverlay(timer, { text: text }) + toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing); } function formatTime(time) { @@ -127,54 +151,79 @@ function formatTime(time) { } function moveUI() { - var relative = { x: 30, y: 90 }; + var relative = { x: 70, y: 40 }; toolBar.move(relative.x, windowDimensions.y - relative.y); Overlays.editOverlay(timer, { - x: relative.x - 10, - y: windowDimensions.y - relative.y - 35, - width: 0, - height: 0 + x: relative.x + timerOffset - ToolBar.SPACING, + y: windowDimensions.y - relative.y - ToolBar.SPACING }); } function mousePressEvent(event) { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - - if (recordIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isPlaying()) { - if (!MyAvatar.isRecording()) { - MyAvatar.startRecording(); - toolBar.setBack(COLOR_ON, ALPHA_ON); - } else { - MyAvatar.stopRecording(); - MyAvatar.loadLastRecording(); - toolBar.setBack(COLOR_OFF, ALPHA_OFF); - } - } else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { - if (MyAvatar.isPlaying()) { - MyAvatar.stopPlaying(); - } else { - MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); - MyAvatar.setPlayerLoop(loop); - MyAvatar.startPlaying(true); - } - } else if (saveIcon === toolBar.clicked(clickedOverlay)) { - if (!MyAvatar.isRecording()) { - recordingFile = Window.save("Save recording to file", ".", "*.rec"); - if (recordingFile != null) { + + if (recordIcon === toolBar.clicked(clickedOverlay, false) && !MyAvatar.isPlaying()) { + if (!MyAvatar.isRecording()) { + MyAvatar.startRecording(); + toolBar.setAlpha(ALPHA_OFF, playIcon); + toolBar.setAlpha(ALPHA_OFF, playLoopIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + } else { + MyAvatar.stopRecording(); + MyAvatar.loadLastRecording(); + toolBar.setAlpha(ALPHA_ON, playIcon); + toolBar.setAlpha(ALPHA_ON, playLoopIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } + } else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { + if (MyAvatar.isPlaying()) { + MyAvatar.stopPlaying(); + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } else { + MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); + MyAvatar.setPlayerLoop(false); + MyAvatar.startPlaying(); + toolBar.setAlpha(ALPHA_OFF, recordIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + watchStop = true; + } + } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { + if (MyAvatar.isPlaying()) { + MyAvatar.stopPlaying(); + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } else { + MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); + MyAvatar.setPlayerLoop(true); + MyAvatar.startPlaying(); + toolBar.setAlpha(ALPHA_OFF, recordIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + } + } else if (saveIcon === toolBar.clicked(clickedOverlay)) { + if (!MyAvatar.isRecording() && !MyAvatar.isPlaying() && MyAvatar.playerLength() != 0) { + recordingFile = Window.save("Save recording to file", ".", "*.rec"); + if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { MyAvatar.saveRecording(recordingFile); - } - } - } else if (loadIcon === toolBar.clicked(clickedOverlay)) { - if (!MyAvatar.isRecording()) { - recordingFile = Window.browse("Load recorcding from file", ".", "*.rec"); - if (recordingFile != "null") { - MyAvatar.loadRecording(recordingFile); - } - } - } else { - - } + } + } + } else if (loadIcon === toolBar.clicked(clickedOverlay)) { + if (!MyAvatar.isRecording() && !MyAvatar.isPlaying()) { + recordingFile = Window.browse("Load recorcding from file", ".", "*.rec"); + if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { + MyAvatar.loadRecording(recordingFile); + } + } + } else { + + } } function update() { @@ -186,6 +235,13 @@ function update() { } updateTimer(); + + if (watchStop && !MyAvatar.isPlaying()) { + watchStop = false; + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } } function scriptEnding() { From ef51782f926a912ec11fd2c90beb7efe1f13034c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 Sep 2014 20:32:18 -0700 Subject: [PATCH 31/51] New ControlACs UI --- examples/ControlACs.js | 149 ++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 68 deletions(-) diff --git a/examples/ControlACs.js b/examples/ControlACs.js index 43eb3290a4..9961db6791 100644 --- a/examples/ControlACs.js +++ b/examples/ControlACs.js @@ -11,7 +11,7 @@ // Set the following variables to the right value var NUM_AC = 3; // This is the number of AC. Their ID need to be unique and between 0 (included) and NUM_AC (excluded) -var NAMES = new Array("Arnold", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1)) +var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1)) // Those variables MUST be common to every scripts var controlVoxelSize = 0.25; @@ -40,9 +40,10 @@ var windowDimensions = Controller.getViewportDimensions(); var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/"; var ALPHA_ON = 1.0; var ALPHA_OFF = 0.7; -var COLOR_TOOL_BAR = { red: 128, green: 128, blue: 128 }; -var COLOR_MASTER = { red: 200, green: 200, blue: 200 }; -var TEXT_HEIGHT = 10; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; +var COLOR_MASTER = { red: 0, green: 0, blue: 0 }; +var TEXT_HEIGHT = 12; +var TEXT_MARGIN = 3; var toolBars = new Array(); var nameOverlays = new Array(); @@ -52,76 +53,87 @@ var playLoopIcon = new Array(); var stopIcon = new Array(); setupToolBars(); + function setupToolBars() { if (toolBars.length > 0) { print("Multiple calls to Recorder.js:setupToolBars()"); return; } - + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + for (i = 0; i <= NUM_AC; i++) { toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL)); - nameOverlays.push(Overlays.addOverlay("text", { - font: { size: TEXT_HEIGHT }, - text: (i === NUM_AC) ? "Master" : - ((i < NAMES.length) ? NAMES[i] : - "AC" + (i + 1)), - x: 0, y: 0, - width: 0, - height: 0, - alpha: 1.0, - visible: true - })); - + toolBars[i].setBack((i === NUM_AC) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF); + onOffIcon.push(toolBars[i].addTool({ - imageURL: TOOL_ICON_URL + "models-tool.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, true, false)); - playIcon.push(null); - playLoopIcon.push(null); - stopIcon.push(null); + imageURL: TOOL_ICON_URL + "ac-on-off.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_ON, + visible: true + }, true, false)); + + playIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "play-pause.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + var playLoopWidthFactor = 1.65; + playLoopIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "play-and-loop.svg", + subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + stopIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "recording-stop.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + nameOverlays.push(Overlays.addOverlay("text", { + backgroundColor: { red: 0, green: 0, blue: 0 }, + font: { size: TEXT_HEIGHT }, + text: (i === NUM_AC) ? "Master" : i + ". " + + ((i < NAMES.length) ? NAMES[i] : + "AC" + i), + x: 0, y: 0, + width: toolBars[i].width + ToolBar.SPACING, + height: TEXT_HEIGHT + TEXT_MARGIN, + leftMargin: TEXT_MARGIN, + topMargin: TEXT_MARGIN, + alpha: ALPHA_OFF, + visible: true + })); } } function sendCommand(id, action) { - if (action === SHOW && toolBars[id].numberOfTools() === 1) { + if (action === SHOW) { toolBars[id].selectTool(onOffIcon[id], true); - - playIcon[id] = toolBars[id].addTool({ - imageURL: TOOL_ICON_URL + "play.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false); - - playLoopIcon[id] = toolBars[id].addTool({ - imageURL: TOOL_ICON_URL + "play-loop.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false); - - stopIcon[id] = toolBars[id].addTool({ - imageURL: TOOL_ICON_URL + "stop.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: ALPHA_ON, - visible: true - }, false); - - toolBars[id].setBack((id === NUM_AC) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF); - } else if (action === HIDE && toolBars[id].numberOfTools() != 1) { + toolBars[id].setAlpha(ALPHA_ON, playIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]); + } else if (action === HIDE) { toolBars[id].selectTool(onOffIcon[id], false); - toolBars[id].removeLastTool(); - toolBars[id].removeLastTool(); - toolBars[id].removeLastTool(); - toolBars[id].setBack(null); - } + toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]); + } else if (!toolBars[id].toolSelected(onOffIcon[id])) { + return; + } if (id === toolBars.length - 1) { for (i = 0; i < NUM_AC; i++) { @@ -179,17 +191,18 @@ function mousePressEvent(event) { } function moveUI() { - var relative = { x: 70, y: 400 }; + var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN; + var relative = { x: 70, y: 75 + (NUM_AC) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize) }; + for (i = 0; i <= NUM_AC; i++) { toolBars[i].move(relative.x, - windowDimensions.y - relative.y + - i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT)); + windowDimensions.y - relative.y + + i * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize)); + Overlays.editOverlay(nameOverlays[i], { - x: relative.x, - y: windowDimensions.y - relative.y + - i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT) - - ToolBar.SPACING - 2 * TEXT_HEIGHT - }); + x: toolBars[i].x - ToolBar.SPACING, + y: toolBars[i].y - textSize + }); } } From e338c600c33d13e982a7e55bfad73c7a088cad3f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 Sep 2014 09:29:29 -0700 Subject: [PATCH 32/51] add debug to show that domain connection is being attempted --- libraries/networking/src/NodeList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 8e32a2f0c2..d77ef321a8 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -343,6 +343,10 @@ void NodeList::sendDomainServerCheckIn() { PacketType domainPacketType = !_domainHandler.isConnected() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest; + if (!_domainHandler.isConnected()) { + qDebug() << "Sending connect request to domain-server at" << _domainHandler.getHostname(); + } + // construct the DS check in packet QUuid packetUUID = _sessionUUID; From 957991b67e76fb433354edf7ad4553000f4a7a93 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 Sep 2014 09:52:26 -0700 Subject: [PATCH 33/51] patch for possible static memory corruption on large edit entity messages --- .../entities/src/EntityEditPacketSender.cpp | 4 ++-- libraries/entities/src/EntityItemProperties.cpp | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index f66ef7ed68..f2588d0493 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -32,7 +32,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemI } // use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize - static unsigned char bufferOut[MAX_PACKET_SIZE]; + unsigned char bufferOut[MAX_PACKET_SIZE]; int sizeOut = 0; if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, &bufferOut[0], _maxPacketSize, sizeOut)) { @@ -45,7 +45,7 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI return; // bail early } // use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize - static unsigned char bufferOut[MAX_PACKET_SIZE]; + unsigned char bufferOut[MAX_PACKET_SIZE]; size_t sizeOut = 0; if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, &bufferOut[0], _maxPacketSize, sizeOut)) { queueOctreeEditMessage(PacketTypeEntityErase, bufferOut, sizeOut); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 536dcea993..65babb6e16 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -596,8 +596,14 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem packetData->endSubTree(); const unsigned char* finalizedData = packetData->getFinalizedData(); int finalizedSize = packetData->getFinalizedSize(); - memcpy(bufferOut, finalizedData, finalizedSize); - sizeOut = finalizedSize; + if (finalizedSize <= sizeIn) { + memcpy(bufferOut, finalizedData, finalizedSize); + sizeOut = finalizedSize; + } else { + qDebug() << "ERROR - encoded edit message doesn't fit in output buffer."; + sizeOut = 0; + success = false; + } } else { packetData->discardSubTree(); sizeOut = 0; @@ -747,8 +753,13 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt unsigned char* outputBuffer, size_t maxLength, size_t& outputLength) { unsigned char* copyAt = outputBuffer; - uint16_t numberOfIds = 1; // only one entity ID in this message + + if (maxLength < sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID) { + qDebug() << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!"; + outputLength = 0; + return false; + } memcpy(copyAt, &numberOfIds, sizeof(numberOfIds)); copyAt += sizeof(numberOfIds); outputLength = sizeof(numberOfIds); From 1e19566e93ad7edbe2fada174a767dcd532925d4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Sep 2014 09:53:07 -0700 Subject: [PATCH 34/51] fix for bad avatar bounding shape on resize --- interface/src/avatar/SkeletonModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9dd299c4df..e54a8e0f42 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -759,7 +759,9 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); + + glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); + _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); } From c0a7334010c502e167ebce763ae91c17aafa8036 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 Sep 2014 11:34:02 -0700 Subject: [PATCH 35/51] Fixed flipped icons --- examples/ControlACs.js | 70 +++++++++++++++++++++--------------------- examples/Recorder.js | 13 ++++++-- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/examples/ControlACs.js b/examples/ControlACs.js index 9961db6791..746a9ddbb3 100644 --- a/examples/ControlACs.js +++ b/examples/ControlACs.js @@ -74,7 +74,7 @@ function setupToolBars() { height: Tool.IMAGE_HEIGHT, alpha: ALPHA_ON, visible: true - }, true, false)); + }, true, true)); playIcon[i] = toolBars[i].addTool({ imageURL: TOOL_ICON_URL + "play-pause.svg", @@ -122,16 +122,16 @@ function setupToolBars() { function sendCommand(id, action) { if (action === SHOW) { - toolBars[id].selectTool(onOffIcon[id], true); + toolBars[id].selectTool(onOffIcon[id], false); toolBars[id].setAlpha(ALPHA_ON, playIcon[id]); toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]); toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]); } else if (action === HIDE) { - toolBars[id].selectTool(onOffIcon[id], false); + toolBars[id].selectTool(onOffIcon[id], true); toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]); toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]); toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]); - } else if (!toolBars[id].toolSelected(onOffIcon[id])) { + } else if (toolBars[id].toolSelected(onOffIcon[id])) { return; } @@ -153,41 +153,41 @@ function sendCommand(id, action) { function mousePressEvent(event) { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - + // Check master control var i = toolBars.length - 1; - if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) { - if (toolBars[i].toolSelected(onOffIcon[i])) { - sendCommand(i, SHOW); - } else { - sendCommand(i, HIDE); - } - } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, PLAY); - } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, PLAY_LOOP); - } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, STOP); - } else { - // Check individual controls + if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + if (toolBars[i].toolSelected(onOffIcon[i])) { + sendCommand(i, SHOW); + } else { + sendCommand(i, HIDE); + } + } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY); + } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY_LOOP); + } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, STOP); + } else { + // Check individual controls for (i = 0; i < NUM_AC; i++) { - if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) { - if (toolBars[i].toolSelected(onOffIcon[i])) { - sendCommand(i, SHOW); - } else { - sendCommand(i, HIDE); - } - } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, PLAY); - } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, PLAY_LOOP); - } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) { - sendCommand(i, STOP); - } else { - - } + if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + if (toolBars[i].toolSelected(onOffIcon[i], false)) { + sendCommand(i, SHOW); + } else { + sendCommand(i, HIDE); + } + } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY); + } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY_LOOP); + } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, STOP); + } else { + + } } - } + } } function moveUI() { diff --git a/examples/Recorder.js b/examples/Recorder.js index bb42e4b292..c32e97ffa5 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -57,7 +57,7 @@ function setupToolBar() { height: Tool.IMAGE_HEIGHT, alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON, visible: true - }, true, MyAvatar.isRecording()); + }, true, !MyAvatar.isRecording()); playIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-pause.svg", @@ -166,12 +166,14 @@ function mousePressEvent(event) { if (recordIcon === toolBar.clicked(clickedOverlay, false) && !MyAvatar.isPlaying()) { if (!MyAvatar.isRecording()) { MyAvatar.startRecording(); + toolBar.selectTool(recordIcon, false); toolBar.setAlpha(ALPHA_OFF, playIcon); toolBar.setAlpha(ALPHA_OFF, playLoopIcon); toolBar.setAlpha(ALPHA_OFF, saveIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon); } else { MyAvatar.stopRecording(); + toolBar.selectTool(recordIcon, true ); MyAvatar.loadLastRecording(); toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon); @@ -184,7 +186,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); - } else { + } else if (MyAvatar.playerLength() > 0) { MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); MyAvatar.setPlayerLoop(false); MyAvatar.startPlaying(); @@ -199,7 +201,7 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); - } else { + } else if (MyAvatar.playerLength() > 0) { MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation); MyAvatar.setPlayerLoop(true); MyAvatar.startPlaying(); @@ -220,6 +222,11 @@ function mousePressEvent(event) { if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { MyAvatar.loadRecording(recordingFile); } + if (MyAvatar.playerLength() > 0) { + toolBar.setAlpha(ALPHA_ON, playIcon); + toolBar.setAlpha(ALPHA_ON, playLoopIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + } } } else { From ac45a56bf9419c46f908a18eae2a4ab4cae09737 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 Sep 2014 11:40:32 -0700 Subject: [PATCH 36/51] tweaks to butterflies, animation, plus reformatting to match coding standard --- examples/entitiesButterflyFlock.js | 469 +++++++++++++++-------------- 1 file changed, 240 insertions(+), 229 deletions(-) diff --git a/examples/entitiesButterflyFlock.js b/examples/entitiesButterflyFlock.js index a715c33c59..b86528bda1 100644 --- a/examples/entitiesButterflyFlock.js +++ b/examples/entitiesButterflyFlock.js @@ -1,230 +1,241 @@ -// -// butterflyFlockTest1.js -// -// -// Created by Adrian McCarlie on August 2, 2014 -// Modified by Brad Hefta-Gaub to use Entities on Sept. 3, 2014 -// Copyright 2014 High Fidelity, Inc. -// -// This sample script creates a swarm of butterfly entities that fly around the avatar. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -function getRandomFloat(min, max) { - return Math.random() * (max - min) + min; -} - -// Multiply vector by scalar -function vScalarMult(v, s) { - var rval = { x: v.x * s, y: v.y * s, z: v.z * s }; - return rval; -} - -function printVector(v) { - print(v.x + ", " + v.y + ", " + v.z + "\n"); -} -// Create a random vector with individual lengths between a,b -function randVector(a, b) { - var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; - return rval; -} - -// Returns a vector which is fraction of the way between a and b -function vInterpolate(a, b, fraction) { - var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; - return rval; -} - -var startTimeInSeconds = new Date().getTime() / 1000; - -var lifeTime = 60; // lifetime of the butterflies in seconds! -var range = 1.0; // Over what distance in meters do you want the flock to fly around -var frame = 0; - -var CHANCE_OF_MOVING = 0.9; -var BUTTERFLY_GRAVITY = 0;//-0.06; -var BUTTERFLY_FLAP_SPEED = 1.0; -var BUTTERFLY_VELOCITY = 0.55; -var myPosition = MyAvatar.position; - -var pitch = 0.0;//experimental -var yaw = 0.0;//experimental -var roll = 0.0; //experimental -var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);//experimental - -// This is our butterfly object -function defineButterfly(entityID, targetPosition) { - this.entityID = entityID; - this.previousFlapOffset = 0; - this.targetPosition = targetPosition; - this.moving = false; -} - -// Array of butterflies -var butterflies = []; -var numButterflies = 20; -function addButterfly() { - // Decide the size of butterfly - var color = { red: 100, green: 100, blue: 100 }; - var size = 0; - - var which = Math.random(); - if (which < 0.2) { - size = 0.08; - } else if (which < 0.4) { - size = 0.09; - } else if (which < 0.6) { - size = 0.8; - } else if (which < 0.8) { - size = 0.8; - } else { - size = 0.8; - } - - myPosition = MyAvatar.position; - // if ( frame < numButterflies){ - // myPosition = {x: myPosition.x, y: myPosition.y, z: myPosition.z }; - // } - - var properties = { - type: "Model", - lifetime: lifeTime, - position: Vec3.sum(randVector(-range, range), myPosition), - velocity: { x: 0, y: 0.0, z: 0 }, - gravity: { x: 0, y: 1.0, z: 0 }, - damping: 0.1, - radius : size, - color: color, - rotation: rotation, - //animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly6.fbx", - //animationIsPlaying: true, - modelURL: "http://business.ozblog.me/objects/butterfly/newButterfly6.fbx" - }; - properties.position.z = properties.position.z+1; - butterflies.push(new defineButterfly(Entities.addEntity(properties), properties.position)); -} - -// Generate the butterflies -for (var i = 0; i < numButterflies; i++) { - addButterfly(); -} - -// Main update function -function updateButterflies(deltaTime) { - // Check to see if we've been running long enough that our butterflies are dead - var nowTimeInSeconds = new Date().getTime() / 1000; - if ((nowTimeInSeconds - startTimeInSeconds) >= lifeTime) { - // print("our butterflies are dying, stop our script"); - Script.stop(); - return; - } - - frame++; - // Only update every third frame - if ((frame % 3) == 0) { - myPosition = MyAvatar.position; - - // Update all the butterflies - for (var i = 0; i < numButterflies; i++) { - entityID = butterflies[i].entityID; - var properties = Entities.getEntityProperties(entityID); - - if (properties.position.y > myPosition.y + getRandomFloat(0.0,0.3)){ //0.3 //ceiling - properties.gravity.y = - 3.0; - properties.damping.y = 1.0; - properties.velocity.y = 0; - properties.velocity.x = properties.velocity.x; - properties.velocity.z = properties.velocity.z; - if (properties.velocity.x < 0.5){ - butterflies[i].moving = false; - } - if (properties.velocity.z < 0.5){ - butterflies[i].moving = false; - } - } - - if (properties.velocity.y <= -0.2) { - properties.velocity.y = 0.22; - properties.velocity.x = properties.velocity.x; - properties.velocity.z = properties.velocity.z; - } - - if (properties.position.y < myPosition.y - getRandomFloat(0.0,0.3)) { //-0.3 // floor - properties.velocity.y = 0.9; - properties.gravity.y = - 4.0; - properties.velocity.x = properties.velocity.x; - properties.velocity.z = properties.velocity.z; - if (properties.velocity.x < 0.5){ - butterflies[i].moving = false; - } - if (properties.velocity.z < 0.5){ - butterflies[i].moving = false; - } - } - - - // Begin movement by getting a target - if (butterflies[i].moving == false) { - if (Math.random() < CHANCE_OF_MOVING) { - var targetPosition = Vec3.sum(randVector(-range, range), myPosition); - if (targetPosition.x < 0) { - targetPosition.x = 0; - } - if (targetPosition.y < 0) { - targetPosition.y = 0; - } - if (targetPosition.z < 0) { - targetPosition.z = 0; - } - if (targetPosition.x > TREE_SCALE) { - targetPosition.x = TREE_SCALE; - } - if (targetPosition.y > TREE_SCALE) { - targetPosition.y = TREE_SCALE; - } - if (targetPosition.z > TREE_SCALE) { - targetPosition.z = TREE_SCALE; - } - butterflies[i].targetPosition = targetPosition; - butterflies[i].moving = true; - } - } - - // If we are moving, move towards the target - if (butterflies[i].moving) { - - var holding = properties.velocity.y; - - var desiredVelocity = Vec3.subtract(butterflies[i].targetPosition, properties.position); - desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BUTTERFLY_VELOCITY); - - properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2); - properties.velocity.y = holding ; - - - // If we are near the target, we should get a new target - if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) { - butterflies[i].moving = false; - } - - var yawRads = Math.atan2(properties.velocity.z, properties.velocity.x); - yawRads = yawRads + Math.PI / 2.0; - var newOrientation = Quat.fromPitchYawRollRadians(0.0, yawRads, 0.0); - properties.rotation = newOrientation; - } - - // Use a cosine wave offset to make it look like its flapping. - var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius); - properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset); - // Change position relative to previous offset. - butterflies[i].previousFlapOffset = offset; - Entities.editEntity(entityID, properties); - } - } -} - -// register the call back so it fires before each data send +// +// butterflyFlockTest1.js +// +// +// Created by Adrian McCarlie on August 2, 2014 +// Modified by Brad Hefta-Gaub to use Entities on Sept. 3, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// This sample script creates a swarm of butterfly entities that fly around the avatar. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +function getRandomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +// Multiply vector by scalar +function vScalarMult(v, s) { + var rval = { x: v.x * s, y: v.y * s, z: v.z * s }; + return rval; +} + +function printVector(v) { + print(v.x + ", " + v.y + ", " + v.z + "\n"); +} +// Create a random vector with individual lengths between a,b +function randVector(a, b) { + var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; + return rval; +} + +// Returns a vector which is fraction of the way between a and b +function vInterpolate(a, b, fraction) { + var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; + return rval; +} + +var startTimeInSeconds = new Date().getTime() / 1000; + +var lifeTime = 60; // lifetime of the butterflies in seconds! +var range = 1.0; // Over what distance in meters do you want the flock to fly around +var frame = 0; + +var CHANCE_OF_MOVING = 0.9; +var BUTTERFLY_GRAVITY = 0;//-0.06; +var BUTTERFLY_FLAP_SPEED = 1.0; +var BUTTERFLY_VELOCITY = 0.55; +var DISTANCE_IN_FRONT_OF_ME = 1.5; +var DISTANCE_ABOVE_ME = 1.5; +var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + + +// set these pitch, yaw, roll to the needed values to orient the model as you want it +var pitchInDegrees = 270.0; +var yawInDegrees = 0.0; +var rollInDegrees = 0.0; +var pitchInRadians = pitchInDegrees / 180.0 * Math.PI; +var yawInRadians = yawInDegrees / 180.0 * Math.PI; +var rollInRadians = rollInDegrees / 180.0 * Math.PI; + +var rotation = Quat.fromPitchYawRollDegrees(pitchInDegrees, yawInDegrees, rollInDegrees);//experimental + +// This is our butterfly object +function defineButterfly(entityID, targetPosition) { + this.entityID = entityID; + this.previousFlapOffset = 0; + this.targetPosition = targetPosition; + this.moving = false; +} + +// Array of butterflies +var butterflies = []; +var numButterflies = 20; +function addButterfly() { + // Decide the size of butterfly + var color = { red: 100, green: 100, blue: 100 }; + var size = 0; + + var which = Math.random(); + if (which < 0.2) { + size = 0.08; + } else if (which < 0.4) { + size = 0.09; + } else if (which < 0.6) { + size = 0.8; + } else if (which < 0.8) { + size = 0.8; + } else { + size = 0.8; + } + + flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + + var properties = { + type: "Model", + lifetime: lifeTime, + position: Vec3.sum(randVector(-range, range), flockPosition), + velocity: { x: 0, y: 0.0, z: 0 }, + gravity: { x: 0, y: 1.0, z: 0 }, + damping: 0.1, + radius : size, + color: color, + rotation: rotation, + animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx", + animationIsPlaying: true, + modelURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx" + }; + properties.position.z = properties.position.z+1; + butterflies.push(new defineButterfly(Entities.addEntity(properties), properties.position)); +} + +// Generate the butterflies +for (var i = 0; i < numButterflies; i++) { + addButterfly(); +} + +// Main update function +function updateButterflies(deltaTime) { + // Check to see if we've been running long enough that our butterflies are dead + var nowTimeInSeconds = new Date().getTime() / 1000; + if ((nowTimeInSeconds - startTimeInSeconds) >= lifeTime) { + // print("our butterflies are dying, stop our script"); + Script.stop(); + return; + } + + frame++; + // Only update every third frame + if ((frame % 3) == 0) { + flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), + Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); + + // Update all the butterflies + for (var i = 0; i < numButterflies; i++) { + entityID = butterflies[i].entityID; + var properties = Entities.getEntityProperties(entityID); + + if (properties.position.y > flockPosition.y + getRandomFloat(0.0,0.3)){ //0.3 //ceiling + properties.gravity.y = - 3.0; + properties.damping.y = 1.0; + properties.velocity.y = 0; + properties.velocity.x = properties.velocity.x; + properties.velocity.z = properties.velocity.z; + if (properties.velocity.x < 0.5){ + butterflies[i].moving = false; + } + if (properties.velocity.z < 0.5){ + butterflies[i].moving = false; + } + } + + if (properties.velocity.y <= -0.2) { + properties.velocity.y = 0.22; + properties.velocity.x = properties.velocity.x; + properties.velocity.z = properties.velocity.z; + } + + if (properties.position.y < flockPosition.y - getRandomFloat(0.0,0.3)) { //-0.3 // floor + properties.velocity.y = 0.9; + properties.gravity.y = - 4.0; + properties.velocity.x = properties.velocity.x; + properties.velocity.z = properties.velocity.z; + if (properties.velocity.x < 0.5){ + butterflies[i].moving = false; + } + if (properties.velocity.z < 0.5){ + butterflies[i].moving = false; + } + } + + + // Begin movement by getting a target + if (butterflies[i].moving == false) { + if (Math.random() < CHANCE_OF_MOVING) { + var targetPosition = Vec3.sum(randVector(-range, range), flockPosition); + if (targetPosition.x < 0) { + targetPosition.x = 0; + } + if (targetPosition.y < 0) { + targetPosition.y = 0; + } + if (targetPosition.z < 0) { + targetPosition.z = 0; + } + if (targetPosition.x > TREE_SCALE) { + targetPosition.x = TREE_SCALE; + } + if (targetPosition.y > TREE_SCALE) { + targetPosition.y = TREE_SCALE; + } + if (targetPosition.z > TREE_SCALE) { + targetPosition.z = TREE_SCALE; + } + butterflies[i].targetPosition = targetPosition; + butterflies[i].moving = true; + } + } + + // If we are moving, move towards the target + if (butterflies[i].moving) { + + var holding = properties.velocity.y; + + var desiredVelocity = Vec3.subtract(butterflies[i].targetPosition, properties.position); + desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BUTTERFLY_VELOCITY); + + properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2); + properties.velocity.y = holding ; + + + // If we are near the target, we should get a new target + if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) { + butterflies[i].moving = false; + } + + var yawRads = Math.atan2(properties.velocity.z, properties.velocity.x); + yawRads = yawRads + Math.PI / 2.0; + var newOrientation = Quat.fromPitchYawRollRadians(pitchInRadians, yawRads, rollInRadians); + properties.rotation = newOrientation; + } + + // Use a cosine wave offset to make it look like its flapping. + var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius); + properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset); + // Change position relative to previous offset. + butterflies[i].previousFlapOffset = offset; + Entities.editEntity(entityID, properties); + } + } +} + +// register the call back so it fires before each data send Script.update.connect(updateButterflies); \ No newline at end of file From d7e135c831c210ae973ce50c14b6a7cdf4c8d538 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 Sep 2014 14:58:30 -0700 Subject: [PATCH 37/51] Changed 2 icons --- examples/ControlACs.js | 2 +- examples/Recorder.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/ControlACs.js b/examples/ControlACs.js index 746a9ddbb3..1b60a9e848 100644 --- a/examples/ControlACs.js +++ b/examples/ControlACs.js @@ -77,7 +77,7 @@ function setupToolBars() { }, true, true)); playIcon[i] = toolBars[i].addTool({ - imageURL: TOOL_ICON_URL + "play-pause.svg", + imageURL: TOOL_ICON_URL + "play.svg", subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, diff --git a/examples/Recorder.js b/examples/Recorder.js index c32e97ffa5..b3a4c7a4c3 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -59,16 +59,15 @@ function setupToolBar() { visible: true }, true, !MyAvatar.isRecording()); + var playLoopWidthFactor = 1.65; playIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-pause.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: Tool.IMAGE_WIDTH, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); - var playLoopWidthFactor = 1.65; playLoopIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-and-loop.svg", subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, From e4deb598922ecfd3f0eede302efd7f1be5c34966 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 Sep 2014 15:00:52 -0700 Subject: [PATCH 38/51] fix sphere colors --- examples/editModels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editModels.js b/examples/editModels.js index fc9c8617ef..9f73b30682 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -2862,7 +2862,7 @@ function handeMenuEvent(menuItem) { properties.gravity.z = array[index++].value; properties.lifetime = array[index++].value; // give ourselves that many more seconds - if (properties.type == "Box") { + if (properties.type == "Box" || properties.type == "Sphere") { properties.color.red = array[index++].value; properties.color.green = array[index++].value; properties.color.blue = array[index++].value; From faf31f268ec197201b4f05e3308ecb9e57c23735 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Sep 2014 15:20:07 -0700 Subject: [PATCH 39/51] use QHash, not QVector, for Octree content query --- interface/src/avatar/VoxelShapeManager.cpp | 94 +++++++--------------- interface/src/avatar/VoxelShapeManager.h | 7 +- libraries/octree/src/Octree.cpp | 16 +++- libraries/octree/src/Octree.h | 4 +- 4 files changed, 53 insertions(+), 68 deletions(-) diff --git a/interface/src/avatar/VoxelShapeManager.cpp b/interface/src/avatar/VoxelShapeManager.cpp index 782fa3fa71..a73679a2c4 100644 --- a/interface/src/avatar/VoxelShapeManager.cpp +++ b/interface/src/avatar/VoxelShapeManager.cpp @@ -29,11 +29,12 @@ void VoxelShapeManager::stepForward(float deltaTime) { if (simulation) { glm::vec3 simulationOrigin = simulation->getTranslation(); if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) { - int numVoxels = _voxels.size(); - for (int i = 0; i < numVoxels; ++i) { + VoxelPool::const_iterator voxelItr = _voxels.constBegin(); + while (voxelItr != _voxels.constEnd()) { // the shape's position is stored in the simulation-frame - glm::vec3 cubeCenter = _voxels[i]._cube.calcCenter(); - _voxels[i]._shape->setTranslation(cubeCenter - simulationOrigin); + const VoxelInfo& voxel = voxelItr.value(); + voxel._shape->setTranslation(voxel._cube.calcCenter() - simulationOrigin); + ++voxelItr; } _lastSimulationTranslation = simulationOrigin; } @@ -44,9 +45,10 @@ void VoxelShapeManager::buildShapes() { // the shapes are owned by the elements of _voxels, // so _shapes is constructed by harvesting them from _voxels _shapes.clear(); - int numVoxels = _voxels.size(); - for (int i = 0; i < numVoxels; ++i) { - _shapes.push_back(_voxels[i]._shape); + VoxelPool::const_iterator voxelItr = _voxels.constBegin(); + while (voxelItr != _voxels.constEnd()) { + _shapes.push_back(voxelItr.value()._shape); + ++voxelItr; } } @@ -60,72 +62,38 @@ void VoxelShapeManager::updateVoxels(CubeList& cubes) { if (!simulation) { return; } - // sort incoming cubes - qSort(cubes); - // Some of the cubes are new, others already exist, and some old voxels no longer exist. - // Since both lists are sorted we walk them simultaneously looking for matches. - QVector cubesToAdd; - QVector voxelsToRemove; - int numCubes = cubes.size(); - int numVoxels = _voxels.size(); - int j = 0; - for (int i = 0; i < numCubes; ++i) { - while (j < numVoxels && _voxels[j]._cube < cubes[i]) { - // remove non-matching voxels not found in cubes - voxelsToRemove.push_back(j++); - } - if (j < numVoxels) { - if (glm::distance2(cubes[i].getCorner(), _voxels[j]._cube.getCorner()) < EPSILON) { - if (cubes[i].getScale() != _voxels[j]._cube.getScale()) { - // the voxel changed scale so we replace - voxelsToRemove.push_back(j++); - cubesToAdd.push_back(i); - } else { - // the voxel already exists - ++j; - } - } else { - // the voxel doesn't exist yet - cubesToAdd.push_back(i); - } + int numChanges = 0; + VoxelPool::iterator voxelItr = _voxels.begin(); + while (voxelItr != _voxels.end()) { + // look for this voxel in cubes + CubeList::iterator cubeItr = cubes.find(voxelItr.key()); + if (cubeItr == cubes.end()) { + // did not find it --> remove the voxel + simulation->removeShape(voxelItr.value()._shape); + voxelItr = _voxels.erase(voxelItr); + ++numChanges; } else { - // all existing voxels have already been processed, so this one is new - cubesToAdd.push_back(i); + // found it --> remove the cube + cubes.erase(cubeItr); + voxelItr++; } } - while (j < numVoxels) { - // remove non-matching voxels at the end - voxelsToRemove.push_back(j++); - } - // remove voxels identified as old, from back to front - for (int i = voxelsToRemove.size() - 1; i >= 0; --i) { - int k = voxelsToRemove[i]; - simulation->removeShape(_voxels[k]._shape); - if (k < numVoxels - 1) { - // copy the last voxel into this spot - _voxels[k] = _voxels[numVoxels - 1]; - } - _voxels.pop_back(); - --numVoxels; - } - - // add new voxels + // add remaining cubes to _voxels glm::vec3 simulationOrigin = simulation->getTranslation(); - for (int i = 0; i < cubesToAdd.size(); ++i) { - const AACube& cube = cubes[cubesToAdd[i]]; - // NOTE: shape's position is in simulation frame + CubeList::const_iterator cubeItr = cubes.constBegin(); + while (cubeItr != cubes.constEnd()) { + AACube cube = cubeItr.value(); AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin); shape->setEntity(this); - VoxelInfo voxel = { cube, shape }; - _voxels.push_back(voxel); - ++numVoxels; + VoxelInfo voxel = {cube, shape }; + _voxels.insert(cubeItr.key(), voxel); + ++numChanges; + ++cubeItr; } - if (cubesToAdd.size() > 0 || voxelsToRemove.size() > 0) { - // keep _voxels sorted - qSort(_voxels); + if (numChanges > 0) { buildShapes(); } } diff --git a/interface/src/avatar/VoxelShapeManager.h b/interface/src/avatar/VoxelShapeManager.h index d5642801b9..1b7179788d 100644 --- a/interface/src/avatar/VoxelShapeManager.h +++ b/interface/src/avatar/VoxelShapeManager.h @@ -12,6 +12,8 @@ #ifndef hifi_VoxelShapeManager_h #define hifi_VoxelShapeManager_h +#include + #include #include #include @@ -22,11 +24,12 @@ class AACubeShape; class VoxelInfo{ public: - bool operator<(const VoxelInfo& otherVoxel) const { return _cube < otherVoxel._cube; } AACube _cube; AACubeShape* _shape; }; +typedef QHash VoxelPool; + class VoxelShapeManager : public PhysicsEntity { public: VoxelShapeManager(); @@ -42,7 +45,7 @@ public: private: glm::vec3 _lastSimulationTranslation; - QVector _voxels; + VoxelPool _voxels; }; #endif // hifi_VoxelShapeManager_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 58e5871aca..3611605515 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -793,6 +793,19 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { return false; } +quint64 cubeListHashKey(const glm::vec3& point) { + // NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits), + // so each component (26 bits) uses more than its alloted 21 bits. + // however we don't expect to span huge cubes so it is ok if we wrap + // (every 2^21 / 2^10 = 2048 meters). + const uint BITS_PER_COMPONENT = 21; + const quint64 MAX_SCALED_COMPONENT = 2097152; // 2^21 + const float RESOLUTION_PER_METER = 1024.0f; // 2^10 + return (quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT + + (((quint64)(point.y * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << BITS_PER_COMPONENT) + + (((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT); +} + bool findContentInCubeOp(OctreeElement* element, void* extraData) { ContentArgs* args = static_cast(extraData); @@ -806,7 +819,8 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) { return true; // recurse on children } if (element->hasContent()) { - args->cubes->push_back(cube); + // NOTE: the voxel's center is unique so we use it as the input for the key + args->cubes->insert(cubeListHashKey(cube.calcCenter()), cube); return true; } return false; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index d877e68737..f58b5f0cbd 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -33,9 +33,9 @@ class Shape; #include +#include #include #include -#include /// derive from this class to use the Octree::recurseTreeWithOperator() method class RecurseOctreeOperator { @@ -48,7 +48,7 @@ public: // Callback function, for recuseTreeWithOperation typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; -typedef QVector CubeList; +typedef QHash CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true; From 7cde6811ca2bcd11fed53aae41e651963e93e8c7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Sep 2014 15:21:19 -0700 Subject: [PATCH 40/51] remove unecessary AACube::operator<() --- libraries/shared/src/AACube.cpp | 7 ------- libraries/shared/src/AACube.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/libraries/shared/src/AACube.cpp b/libraries/shared/src/AACube.cpp index 1bfffb202c..bf8e972455 100644 --- a/libraries/shared/src/AACube.cpp +++ b/libraries/shared/src/AACube.cpp @@ -314,13 +314,6 @@ bool AACube::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end return true; } -bool AACube::operator<(const AACube& otherCube) const { - return (_corner.x < otherCube._corner.x - || (_corner.x == otherCube._corner.x && (_corner.y < otherCube._corner.y - || (_corner.y == otherCube._corner.y && (_corner.z < otherCube._corner.z - || _scale < otherCube._scale))))); -} - glm::vec3 AACube::getClosestPointOnFace(const glm::vec3& point, BoxFace face) const { switch (face) { case MIN_X_FACE: diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index 873f271981..705c025d74 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -60,8 +60,6 @@ public: AABox clamp(const glm::vec3& min, const glm::vec3& max) const; AABox clamp(float min, float max) const; - bool operator<(const AACube& otherCube) const; // for qSorted lists of AACubes - private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; From 27621b316d83a565ccf7c96b6c34787834e0bb9d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Sep 2014 15:41:24 -0700 Subject: [PATCH 41/51] clarify how the size of the cube is computed --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9c6a833da0..9c81e9cf17 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1488,10 +1488,10 @@ static CollisionList myCollisions(64); void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - // compute the bounding cube around avatar - float radius = 2.0f * getBoundingRadius(); - glm::vec3 corner = getPosition() - glm::vec3(radius); - AACube boundingCube(corner, 2.0f * radius); + // We use a multiple of the avatar's boundingRadius as the size of the cube of interest. + float cubeScale = 4.0f * getBoundingRadius(); + glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale); + AACube boundingCube(corner, cubeScale); // query the VoxelTree for cubes that touch avatar's boundingCube CubeList cubes; From 931e5fa4087906b24855bd1d4943932529c501d1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 Sep 2014 15:43:51 -0700 Subject: [PATCH 42/51] fix potential static memory corruption --- libraries/entities/src/EntityItemID.h | 2 -- libraries/entities/src/EntityTypes.cpp | 9 ++++++--- libraries/entities/src/EntityTypes.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 61df01aaf6..7a3f822c7c 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -50,8 +50,6 @@ public: EntityItemID convertToCreatorTokenVersion() const; // these methods allow you to create models, and later edit them. - //static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID); - static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID); static uint32_t getNextCreatorTokenID(); static void handleAddEntityResponse(const QByteArray& packet); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index b89ff66eed..51890ad7d6 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -24,7 +24,7 @@ QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; -EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST]; +EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST + 1]; bool EntityTypes::_factoriesInitialized = false; const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown"; @@ -58,8 +58,11 @@ bool EntityTypes::registerEntityType(EntityType entityType, const char* name, En memset(&_factories,0,sizeof(_factories)); _factoriesInitialized = true; } - _factories[entityType] = factoryMethod; - return true; + if (entityType >= 0 && entityType <= LAST) { + _factories[entityType] = factoryMethod; + return true; + } + return false; } EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 541aeada4f..f6e78275d3 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -45,7 +45,7 @@ public: private: static QMap _typeToNameMap; static QMap _nameToTypeMap; - static EntityTypeFactory _factories[LAST]; + static EntityTypeFactory _factories[LAST + 1]; static bool _factoriesInitialized; }; From 21f3f2b90e7570528d61a77dbb7077de7ab77865 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Sep 2014 17:17:44 -0700 Subject: [PATCH 43/51] Switch to deferred lighting for metavoxels. --- .../resources/shaders/directional_light.frag | 26 + ...irectional_light_cascaded_shadow_map.frag} | 41 +- .../shaders/directional_light_shadow_map.frag | 60 ++ .../shaders/metavoxel_heightfield.frag | 25 - .../shaders/metavoxel_heightfield.vert | 51 -- .../shaders/metavoxel_heightfield_base.frag | 6 +- .../shaders/metavoxel_heightfield_base.vert | 13 +- .../shaders/metavoxel_heightfield_light.frag | 21 - .../shaders/metavoxel_heightfield_light.vert | 45 -- ...heightfield_light_cascaded_shadow_map.frag | 44 -- ...etavoxel_heightfield_light_shadow_map.frag | 33 -- .../metavoxel_heightfield_shadow_map.frag | 37 -- .../shaders/metavoxel_voxel_base.frag | 21 + .../shaders/metavoxel_voxel_base.vert | 26 + .../shaders/metavoxel_voxel_splat.frag | 29 + .../shaders/metavoxel_voxel_splat.vert | 62 +++ interface/src/MetavoxelSystem.cpp | 520 +++++++++++------- interface/src/MetavoxelSystem.h | 104 ++-- interface/src/renderer/TextureCache.cpp | 20 +- interface/src/renderer/TextureCache.h | 4 + interface/src/ui/MetavoxelEditor.cpp | 14 +- interface/src/ui/MetavoxelEditor.h | 3 +- 22 files changed, 670 insertions(+), 535 deletions(-) create mode 100644 interface/resources/shaders/directional_light.frag rename interface/resources/shaders/{metavoxel_heightfield_cascaded_shadow_map.frag => directional_light_cascaded_shadow_map.frag} (50%) create mode 100644 interface/resources/shaders/directional_light_shadow_map.frag delete mode 100644 interface/resources/shaders/metavoxel_heightfield.frag delete mode 100644 interface/resources/shaders/metavoxel_heightfield.vert delete mode 100644 interface/resources/shaders/metavoxel_heightfield_light.frag delete mode 100644 interface/resources/shaders/metavoxel_heightfield_light.vert delete mode 100644 interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag delete mode 100644 interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag delete mode 100644 interface/resources/shaders/metavoxel_heightfield_shadow_map.frag create mode 100644 interface/resources/shaders/metavoxel_voxel_base.frag create mode 100644 interface/resources/shaders/metavoxel_voxel_base.vert create mode 100644 interface/resources/shaders/metavoxel_voxel_splat.frag create mode 100644 interface/resources/shaders/metavoxel_voxel_splat.vert diff --git a/interface/resources/shaders/directional_light.frag b/interface/resources/shaders/directional_light.frag new file mode 100644 index 0000000000..7fb5d83719 --- /dev/null +++ b/interface/resources/shaders/directional_light.frag @@ -0,0 +1,26 @@ +#version 120 + +// +// directional_light.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal texture +uniform sampler2D normalMap; + +void main(void) { + // compute the base color based on OpenGL lighting model + vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + gl_FragColor = vec4((texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor + + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normal * 2.0 - + vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position)))).rgb, normal.a); +} diff --git a/interface/resources/shaders/metavoxel_heightfield_cascaded_shadow_map.frag b/interface/resources/shaders/directional_light_cascaded_shadow_map.frag similarity index 50% rename from interface/resources/shaders/metavoxel_heightfield_cascaded_shadow_map.frag rename to interface/resources/shaders/directional_light_cascaded_shadow_map.frag index 059a4e0296..d16467520e 100644 --- a/interface/resources/shaders/metavoxel_heightfield_cascaded_shadow_map.frag +++ b/interface/resources/shaders/directional_light_cascaded_shadow_map.frag @@ -1,10 +1,10 @@ #version 120 // -// metavoxel_heightfield.frag +// directional_light.frag // fragment shader // -// Created by Andrzej Kapolka on 7/28/14. +// Created by Andrzej Kapolka on 9/3/14. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -14,6 +14,12 @@ // the diffuse texture uniform sampler2D diffuseMap; +// the normal texture +uniform sampler2D normalMap; + +// the depth texture +uniform sampler2D depthMap; + // the shadow texture uniform sampler2DShadow shadowMap; @@ -21,28 +27,39 @@ uniform sampler2DShadow shadowMap; uniform vec3 shadowDistances; // the inverse of the size of the shadow map -const float shadowScale = 1.0 / 2048.0; +uniform float shadowScale; -// the interpolated position -varying vec4 position; +// the distance to the near clip plane +uniform float near; -// the interpolated normal -varying vec4 normal; +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; void main(void) { + // compute the view space position using the depth + float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0); + vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + // compute the index of the cascade to use and the corresponding texture coordinates int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), dot(gl_EyePlaneR[shadowIndex], position)); - // compute the base color based on OpenGL lighting model - float diffuse = dot(normalize(normal), gl_LightSource[0].position); + // compute the color based on OpenGL lighting model, use the alpha from the normal map + vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); - vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); - gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st); + vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor + + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FragColor = vec4(baseColor.rgb, normal.a); } diff --git a/interface/resources/shaders/directional_light_shadow_map.frag b/interface/resources/shaders/directional_light_shadow_map.frag new file mode 100644 index 0000000000..d4ac254fc5 --- /dev/null +++ b/interface/resources/shaders/directional_light_shadow_map.frag @@ -0,0 +1,60 @@ +#version 120 + +// +// directional_light.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal texture +uniform sampler2D normalMap; + +// the depth texture +uniform sampler2D depthMap; + +// the shadow texture +uniform sampler2DShadow shadowMap; + +// the inverse of the size of the shadow map +uniform float shadowScale; + +// the distance to the near clip plane +uniform float near; + +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; + +void main(void) { + // compute the view space position using the depth + float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0); + vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + + // compute the corresponding texture coordinates + vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position)); + + // compute the color based on OpenGL lighting model, use the alpha from the normal map + vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position); + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); + vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor + + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FragColor = vec4(baseColor.rgb, normal.a); +} diff --git a/interface/resources/shaders/metavoxel_heightfield.frag b/interface/resources/shaders/metavoxel_heightfield.frag deleted file mode 100644 index f99a0a6403..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield.frag +++ /dev/null @@ -1,25 +0,0 @@ -#version 120 - -// -// metavoxel_heightfield.frag -// fragment shader -// -// Created by Andrzej Kapolka on 7/28/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the diffuse texture -uniform sampler2D diffuseMap; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // compute the base color based on OpenGL lighting model - vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position))); - gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st); -} diff --git a/interface/resources/shaders/metavoxel_heightfield.vert b/interface/resources/shaders/metavoxel_heightfield.vert deleted file mode 100644 index 70cf3f9419..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield.vert +++ /dev/null @@ -1,51 +0,0 @@ -#version 120 - -// -// metavoxel_heighfield.vert -// vertex shader -// -// Created by Andrzej Kapolka on 7/28/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the height texture -uniform sampler2D heightMap; - -// the distance between height points in texture space -uniform float heightScale; - -// the scale between height and color textures -uniform float colorScale; - -// the interpolated position -varying vec4 position; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // transform and store the normal for interpolation - vec2 heightCoord = gl_MultiTexCoord0.st; - float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r - - texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r; - float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r - - texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; - normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); - - // add the height to the position - float height = texture2D(heightMap, heightCoord).r; - position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); - gl_Position = gl_ProjectionMatrix * position; - - // the zero height should be invisible - gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); - - // pass along the scaled/offset texture coordinates - gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale; - - // and the shadow texture coordinates - gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0); -} diff --git a/interface/resources/shaders/metavoxel_heightfield_base.frag b/interface/resources/shaders/metavoxel_heightfield_base.frag index 9b64a59e6f..a4523ced23 100644 --- a/interface/resources/shaders/metavoxel_heightfield_base.frag +++ b/interface/resources/shaders/metavoxel_heightfield_base.frag @@ -14,7 +14,11 @@ // the diffuse texture uniform sampler2D diffuseMap; +// the interpolated normal +varying vec4 normal; + void main(void) { // compute the base color based on OpenGL lighting model - gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); + gl_FragData[0] = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st); + gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); } diff --git a/interface/resources/shaders/metavoxel_heightfield_base.vert b/interface/resources/shaders/metavoxel_heightfield_base.vert index 3e4b081d6f..f097426e13 100644 --- a/interface/resources/shaders/metavoxel_heightfield_base.vert +++ b/interface/resources/shaders/metavoxel_heightfield_base.vert @@ -20,9 +20,20 @@ uniform float heightScale; // the scale between height and color textures uniform float colorScale; +// the interpolated normal +varying vec4 normal; + void main(void) { + // transform and store the normal for interpolation + vec2 heightCoord = gl_MultiTexCoord0.st; + float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r - + texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r; + float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r - + texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; + normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); + // add the height to the position - float height = texture2D(heightMap, gl_MultiTexCoord0.st).r; + float height = texture2D(heightMap, heightCoord).r; gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); // the zero height should be invisible diff --git a/interface/resources/shaders/metavoxel_heightfield_light.frag b/interface/resources/shaders/metavoxel_heightfield_light.frag deleted file mode 100644 index ce3f23e142..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield_light.frag +++ /dev/null @@ -1,21 +0,0 @@ -#version 120 - -// -// metavoxel_heightfield_light.frag -// fragment shader -// -// Created by Andrzej Kapolka on 8/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // compute the base color based on OpenGL lighting model - gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position))); -} diff --git a/interface/resources/shaders/metavoxel_heightfield_light.vert b/interface/resources/shaders/metavoxel_heightfield_light.vert deleted file mode 100644 index 228d575b81..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield_light.vert +++ /dev/null @@ -1,45 +0,0 @@ -#version 120 - -// -// metavoxel_heighfield_light.vert -// vertex shader -// -// Created by Andrzej Kapolka on 8/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the height texture -uniform sampler2D heightMap; - -// the distance between height points in texture space -uniform float heightScale; - -// the interpolated position -varying vec4 position; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // transform and store the normal for interpolation - vec2 heightCoord = gl_MultiTexCoord0.st; - float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r - - texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r; - float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r - - texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r; - normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0)); - - // add the height to the position - float height = texture2D(heightMap, heightCoord).r; - position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0)); - gl_Position = gl_ProjectionMatrix * position; - - // the zero height should be invisible - gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); - - // and the shadow texture coordinates - gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0); -} diff --git a/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag b/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag deleted file mode 100644 index 73382eb83c..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag +++ /dev/null @@ -1,44 +0,0 @@ -#version 120 - -// -// metavoxel_heightfield_light_cascaded_shadow_map.frag -// fragment shader -// -// Created by Andrzej Kapolka on 8/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the shadow texture -uniform sampler2DShadow shadowMap; - -// the distances to the cascade sections -uniform vec3 shadowDistances; - -// the inverse of the size of the shadow map -const float shadowScale = 1.0 / 2048.0; - -// the interpolated position -varying vec4 position; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // compute the index of the cascade to use and the corresponding texture coordinates - int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); - vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), - dot(gl_EyePlaneR[shadowIndex], position)); - - // compute the base color based on OpenGL lighting model - float diffuse = dot(normalize(normal), gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * 0.25 * - (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r + - shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); - gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); -} diff --git a/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag b/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag deleted file mode 100644 index 4f2df8958b..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield_light_shadow_map.frag +++ /dev/null @@ -1,33 +0,0 @@ -#version 120 - -// -// metavoxel_heightfield_light_shadow_map.frag -// fragment shader -// -// Created by Andrzej Kapolka on 8/20/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the shadow texture -uniform sampler2DShadow shadowMap; - -// the inverse of the size of the shadow map -const float shadowScale = 1.0 / 2048.0; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // compute the base color based on OpenGL lighting model - float diffuse = dot(normalize(normal), gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * 0.25 * - (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); - gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); -} diff --git a/interface/resources/shaders/metavoxel_heightfield_shadow_map.frag b/interface/resources/shaders/metavoxel_heightfield_shadow_map.frag deleted file mode 100644 index bf319462d3..0000000000 --- a/interface/resources/shaders/metavoxel_heightfield_shadow_map.frag +++ /dev/null @@ -1,37 +0,0 @@ -#version 120 - -// -// metavoxel_heightfield.frag -// fragment shader -// -// Created by Andrzej Kapolka on 7/28/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// the diffuse texture -uniform sampler2D diffuseMap; - -// the shadow texture -uniform sampler2DShadow shadowMap; - -// the inverse of the size of the shadow map -const float shadowScale = 1.0 / 2048.0; - -// the interpolated normal -varying vec4 normal; - -void main(void) { - // compute the base color based on OpenGL lighting model - float diffuse = dot(normalize(normal), gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * 0.25 * - (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); - vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); - gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st); -} diff --git a/interface/resources/shaders/metavoxel_voxel_base.frag b/interface/resources/shaders/metavoxel_voxel_base.frag new file mode 100644 index 0000000000..72eca6b25d --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_base.frag @@ -0,0 +1,21 @@ +#version 120 + +// +// metavoxel_voxel_base.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/4/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // store the interpolated color and normal + gl_FragData[0] = gl_Color; + gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); +} diff --git a/interface/resources/shaders/metavoxel_voxel_base.vert b/interface/resources/shaders/metavoxel_voxel_base.vert new file mode 100644 index 0000000000..4fe5802462 --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_base.vert @@ -0,0 +1,26 @@ +#version 120 + +// +// metavoxel_voxel_base.vert +// vertex shader +// +// Created by Andrzej Kapolka on 9/4/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // transform and store the normal for interpolation + normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0); + + // use the fixed-function position + gl_Position = ftransform(); + + // copy the color for interpolation + gl_FrontColor = vec4(gl_Color.rgb, 0.0); +} diff --git a/interface/resources/shaders/metavoxel_voxel_splat.frag b/interface/resources/shaders/metavoxel_voxel_splat.frag new file mode 100644 index 0000000000..66ce2721ac --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_splat.frag @@ -0,0 +1,29 @@ +#version 120 + +// +// metavoxel_voxel_splat.frag +// fragment shader +// +// Created by Andrzej Kapolka on 9/4/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the number of splats per pass +const int SPLAT_COUNT = 4; + +// the splat textures +uniform sampler2D diffuseMaps[SPLAT_COUNT]; + +// alpha values for the four splat textures +varying vec4 alphaValues; + +void main(void) { + // blend the splat textures + gl_FragColor = (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + + texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y + + texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z + + texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w); +} diff --git a/interface/resources/shaders/metavoxel_voxel_splat.vert b/interface/resources/shaders/metavoxel_voxel_splat.vert new file mode 100644 index 0000000000..150a9e7d2e --- /dev/null +++ b/interface/resources/shaders/metavoxel_voxel_splat.vert @@ -0,0 +1,62 @@ +#version 120 + +// +// metavoxel_voxel_splat.vert +// vertex shader +// +// Created by Andrzej Kapolka on 9/4/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the splat textures scales on the S axis +uniform vec4 splatTextureScalesS; + +// the splat texture scales on the T axis +uniform vec4 splatTextureScalesT; + +// the lower bounds of the values corresponding to the splat textures +uniform vec4 textureValueMinima; + +// the upper bounds of the values corresponding to the splat textures +uniform vec4 textureValueMaxima; + +// the materials to apply to the vertex +attribute vec4 materials; + +// the weights of each material +attribute vec4 materialWeights; + +// alpha values for the four splat textures +varying vec4 alphaValues; + +void main(void) { + // use the fixed-function position + gl_Position = ftransform(); + + // pass along the scaled/offset texture coordinates + vec4 textureSpacePosition = vec4(gl_Vertex.xz, 0.0, 1.0); + gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], 0.0, 1.0); + gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], 0.0, 1.0); + gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], 0.0, 1.0); + gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0); + + // compute the alpha values for each texture + float value = materials[0]; + vec4 valueVector = vec4(value, value, value, value); + alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[0]; + + value = materials[1]; + valueVector = vec4(value, value, value, value); + alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[1]; + + value = materials[2]; + valueVector = vec4(value, value, value, value); + alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[2]; + + value = materials[3]; + valueVector = vec4(value, value, value, value); + alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[3]; +} diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index f135579526..d3819bf89b 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -9,7 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include +#include #include #include #include @@ -24,6 +28,7 @@ #include "Application.h" #include "MetavoxelSystem.h" #include "renderer/Model.h" +#include "renderer/RenderUtil.h" REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation) REGISTER_META_OBJECT(SphereRenderer) @@ -46,6 +51,12 @@ void MetavoxelSystem::init() { new BufferDataAttribute("voxelBuffer")); _voxelBufferAttribute->setLODThresholdMultiplier( AttributeRegistry::getInstance()->getVoxelColorAttribute()->getLODThresholdMultiplier()); + + loadLightProgram("shaders/directional_light.frag", _directionalLight, _directionalLightLocations); + loadLightProgram("shaders/directional_light_shadow_map.frag", _directionalLightShadowMap, + _directionalLightShadowMapLocations); + loadLightProgram("shaders/directional_light_cascaded_shadow_map.frag", _directionalLightCascadedShadowMap, + _directionalLightCascadedShadowMapLocations); } MetavoxelLOD MetavoxelSystem::getLOD() { @@ -116,6 +127,10 @@ int RenderVisitor::visit(MetavoxelInfo& info) { return STOP_RECURSION; } +const GLenum COLOR_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT0 }; +const GLenum NORMAL_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT1 }; +const GLenum COLOR_NORMAL_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + void MetavoxelSystem::render() { // update the frustum ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum(); @@ -123,8 +138,128 @@ void MetavoxelSystem::render() { viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); + _needToLight = false; + + // clear the normal buffer + glDrawBuffers(sizeof(NORMAL_DRAW_BUFFERS) / sizeof(NORMAL_DRAW_BUFFERS[0]), NORMAL_DRAW_BUFFERS); + glClear(GL_COLOR_BUFFER_BIT); + + glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS); + RenderVisitor renderVisitor(getLOD()); guideToAugmented(renderVisitor, true); + + // give external parties a chance to join in + emit rendering(); + + if (!_needToLight) { + return; // skip lighting if not needed + } + + // perform deferred lighting, rendering to free fbo + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + + QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject(); + primaryFBO->release(); + + QOpenGLFramebufferObject* freeFBO = Application::getInstance()->getGlowEffect()->getFreeFramebufferObject(); + freeFBO->bind(); + glClear(GL_COLOR_BUFFER_BIT); + + glBindTexture(GL_TEXTURE_2D, primaryFBO->texture()); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryNormalTextureID()); + + if (Menu::getInstance()->getShadowsEnabled()) { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryDepthTextureID()); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); + + ProgramObject* program = &_directionalLightShadowMap; + const LightLocations* locations = &_directionalLightShadowMapLocations; + if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + program = &_directionalLightCascadedShadowMap; + locations = &_directionalLightCascadedShadowMapLocations; + _directionalLightCascadedShadowMap.bind(); + _directionalLightCascadedShadowMap.setUniform(locations->shadowDistances, + Application::getInstance()->getShadowDistances()); + + } else { + program->bind(); + } + program->setUniformValue(locations->shadowScale, + 1.0f / Application::getInstance()->getTextureCache()->getShadowFramebufferObject()->width()); + + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; + Application::getInstance()->computeOffAxisFrustum( + left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + program->setUniformValue(locations->near, nearVal); + program->setUniformValue(locations->depthScale, (farVal - nearVal) / farVal); + float nearScale = -1.0f / nearVal; + program->setUniformValue(locations->depthTexCoordOffset, left * nearScale, bottom * nearScale); + program->setUniformValue(locations->depthTexCoordScale, (right - left) * nearScale, (top - bottom) * nearScale); + + renderFullscreenQuad(); + + program->release(); + + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE1); + + } else { + _directionalLight.bind(); + renderFullscreenQuad(); + _directionalLight.release(); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + freeFBO->release(); + + // now transfer the lit region to the primary fbo + glEnable(GL_BLEND); + + primaryFBO->bind(); + + glBindTexture(GL_TEXTURE_2D, freeFBO->texture()); + glEnable(GL_TEXTURE_2D); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + renderFullscreenQuad(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + + glDisable(GL_ALPHA_TEST); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); } class RayHeightfieldIntersectionVisitor : public RayIntersectionVisitor { @@ -484,6 +619,24 @@ void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { } } +void MetavoxelSystem::loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations) { + program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + name); + program.link(); + + program.bind(); + program.setUniformValue("diffuseMap", 0); + program.setUniformValue("normalMap", 1); + program.setUniformValue("depthMap", 2); + program.setUniformValue("shadowMap", 3); + locations.shadowDistances = program.uniformLocation("shadowDistances"); + locations.shadowScale = program.uniformLocation("shadowScale"); + locations.near = program.uniformLocation("near"); + locations.depthScale = program.uniformLocation("depthScale"); + locations.depthTexCoordOffset = program.uniformLocation("depthTexCoordOffset"); + locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale"); + program.release(); +} + MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : MetavoxelClient(node, updater) { } @@ -807,7 +960,6 @@ void HeightfieldBuffer::render(bool cursor) { glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); } else if (!_materials.isEmpty()) { - DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( @@ -817,6 +969,8 @@ void HeightfieldBuffer::render(bool cursor) { glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); + glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS); + glDepthFunc(GL_LEQUAL); glDepthMask(false); glEnable(GL_BLEND); @@ -825,18 +979,18 @@ void HeightfieldBuffer::render(bool cursor) { glPolygonOffset(-1.0f, -1.0f); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind(); + const DefaultMetavoxelRendererImplementation::SplatLocations& locations = + DefaultMetavoxelRendererImplementation::getSplatHeightfieldLocations(); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatHeightScaleLocation(), 1.0f / _heightSize); + locations.heightScale, 1.0f / _heightSize); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureScaleLocation(), (float)_heightSize / innerSize); + locations.textureScale, (float)_heightSize / innerSize); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureOffsetLocation(), - _translation.x / _scale, _translation.z / _scale); + locations.splatTextureOffset, _translation.x / _scale, _translation.z / _scale); glBindTexture(GL_TEXTURE_2D, _materialTextureID); - const int TEXTURES_PER_SPLAT = 4; - for (int i = 0; i < _materials.size(); i += TEXTURES_PER_SPLAT) { + for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { QVector4D scalesS, scalesT; for (int j = 0; j < SPLAT_COUNT; j++) { @@ -858,23 +1012,20 @@ void HeightfieldBuffer::render(bool cursor) { } const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureScalesSLocation(), scalesS); + locations.splatTextureScalesS, scalesS); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureScalesTLocation(), scalesT); + locations.splatTextureScalesT, scalesT); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureValueMinimaLocation(), + locations.textureValueMinima, (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getSplatTextureValueMaximaLocation(), + locations.textureValueMaxima, (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); } - glEnable(GL_ALPHA_TEST); - glBlendFunc(GL_DST_COLOR, GL_ZERO); - for (int i = 0; i < SPLAT_COUNT; i++) { glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); glBindTexture(GL_TEXTURE_2D, 0); @@ -882,53 +1033,24 @@ void HeightfieldBuffer::render(bool cursor) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); - - if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) { - DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().bind(); - DefaultMetavoxelRendererImplementation::getShadowLightHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getShadowLightHeightScaleLocation(), 1.0f / _heightSize); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - DefaultMetavoxelRendererImplementation::getShadowMapHeightfieldProgram().bind(); - - } else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { - DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().bind(); - DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getCascadedShadowLightHeightScaleLocation(), 1.0f / _heightSize); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - DefaultMetavoxelRendererImplementation::getCascadedShadowMapHeightfieldProgram().bind(); - - } else { - DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().bind(); - DefaultMetavoxelRendererImplementation::getLightHeightfieldProgram().setUniformValue( - DefaultMetavoxelRendererImplementation::getLightHeightScaleLocation(), 1.0f / _heightSize); - glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0); - DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind(); - } - - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glDepthFunc(GL_LESS); - glDepthMask(true); glActiveTexture(GL_TEXTURE0); + glDisable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDepthMask(true); + glDepthFunc(GL_LESS); + + glDrawBuffers(sizeof(COLOR_NORMAL_DRAW_BUFFERS) / sizeof(COLOR_NORMAL_DRAW_BUFFERS[0]), COLOR_NORMAL_DRAW_BUFFERS); + + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); + } else { - int heightScaleLocation = DefaultMetavoxelRendererImplementation::getHeightScaleLocation(); - int colorScaleLocation = DefaultMetavoxelRendererImplementation::getColorScaleLocation(); - ProgramObject* program = &DefaultMetavoxelRendererImplementation::getHeightfieldProgram(); - if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) { - heightScaleLocation = DefaultMetavoxelRendererImplementation::getShadowMapHeightScaleLocation(); - colorScaleLocation = DefaultMetavoxelRendererImplementation::getShadowMapColorScaleLocation(); - program = &DefaultMetavoxelRendererImplementation::getShadowMapHeightfieldProgram(); - - } else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { - heightScaleLocation = DefaultMetavoxelRendererImplementation::getCascadedShadowMapHeightScaleLocation(); - colorScaleLocation = DefaultMetavoxelRendererImplementation::getCascadedShadowMapColorScaleLocation(); - program = &DefaultMetavoxelRendererImplementation::getCascadedShadowMapHeightfieldProgram(); - } - program->setUniformValue(heightScaleLocation, 1.0f / _heightSize); - program->setUniformValue(colorScaleLocation, (float)_heightSize / innerSize); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseHeightScaleLocation(), 1.0f / _heightSize); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().setUniformValue( + DefaultMetavoxelRendererImplementation::getBaseColorScaleLocation(), (float)_heightSize / innerSize); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, _colorTextureID); @@ -944,6 +1066,8 @@ void HeightfieldBuffer::render(bool cursor) { bufferPair.first.release(); bufferPair.second.release(); + + Application::getInstance()->getMetavoxels()->noteNeedToLight(); } QHash HeightfieldBuffer::_bufferPairs; @@ -959,7 +1083,7 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind(); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().bind(); glPushMatrix(); glTranslatef(translation.x, translation.y, translation.z); @@ -971,7 +1095,7 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glPopMatrix(); - DefaultMetavoxelRendererImplementation::getHeightfieldProgram().release(); + DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -1025,8 +1149,89 @@ void VoxelBuffer::render(bool cursor) { glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + if (!_materials.isEmpty()) { + glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS); + + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().bind(); + const DefaultMetavoxelRendererImplementation::SplatLocations& locations = + DefaultMetavoxelRendererImplementation::getSplatVoxelLocations(); + + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materials, + GL_UNSIGNED_BYTE, (qint64)&point->materials, SPLAT_COUNT, sizeof(VoxelPoint)); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materials); + + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setAttributeBuffer(locations.materialWeights, + GL_UNSIGNED_BYTE, (qint64)&point->materialWeights, SPLAT_COUNT, sizeof(VoxelPoint)); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().enableAttributeArray(locations.materialWeights); + + for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { + QVector4D scalesS, scalesT; + + for (int j = 0; j < SPLAT_COUNT; j++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]); + int index = i + j; + if (index < _networkTextures.size()) { + const NetworkTexturePointer& texture = _networkTextures.at(index); + if (texture) { + MaterialObject* material = static_cast(_materials.at(index).data()); + scalesS[j] = 1.0f / material->getScaleS(); + scalesT[j] = 1.0f / material->getScaleT(); + glBindTexture(GL_TEXTURE_2D, texture->getID()); + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } + const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL; + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( + locations.splatTextureScalesS, scalesS); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( + locations.splatTextureScalesT, scalesT); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( + locations.textureValueMinima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().setUniformValue( + locations.textureValueMaxima, + (i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, + (i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP); + glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); + } + + for (int i = 0; i < SPLAT_COUNT; i++) { + glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glActiveTexture(GL_TEXTURE0); + + glDisable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + glDepthMask(true); + glDepthFunc(GL_LESS); + + glDrawBuffers(sizeof(COLOR_NORMAL_DRAW_BUFFERS) / sizeof(COLOR_NORMAL_DRAW_BUFFERS[0]), COLOR_NORMAL_DRAW_BUFFERS); + + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materials); + DefaultMetavoxelRendererImplementation::getSplatVoxelProgram().disableAttributeArray(locations.materialWeights); + + DefaultMetavoxelRendererImplementation::getBaseVoxelProgram().bind(); + } + _vertexBuffer.release(); _indexBuffer.release(); + + Application::getInstance()->getMetavoxels()->noteNeedToLight(); } BufferDataAttribute::BufferDataAttribute(const QString& name) : @@ -1056,48 +1261,6 @@ void DefaultMetavoxelRendererImplementation::init() { _pointScaleLocation = _pointProgram.uniformLocation("pointScale"); _pointProgram.release(); - _heightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield.vert"); - _heightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield.frag"); - _heightfieldProgram.link(); - - _heightfieldProgram.bind(); - _heightfieldProgram.setUniformValue("heightMap", 0); - _heightfieldProgram.setUniformValue("diffuseMap", 1); - _heightScaleLocation = _heightfieldProgram.uniformLocation("heightScale"); - _colorScaleLocation = _heightfieldProgram.uniformLocation("colorScale"); - _heightfieldProgram.release(); - - _shadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield.vert"); - _shadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_shadow_map.frag"); - _shadowMapHeightfieldProgram.link(); - - _shadowMapHeightfieldProgram.bind(); - _shadowMapHeightfieldProgram.setUniformValue("heightMap", 0); - _shadowMapHeightfieldProgram.setUniformValue("diffuseMap", 1); - _shadowMapHeightfieldProgram.setUniformValue("shadowMap", 2); - _shadowMapHeightScaleLocation = _shadowMapHeightfieldProgram.uniformLocation("heightScale"); - _shadowMapColorScaleLocation = _shadowMapHeightfieldProgram.uniformLocation("colorScale"); - _shadowMapHeightfieldProgram.release(); - - _cascadedShadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield.vert"); - _cascadedShadowMapHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_cascaded_shadow_map.frag"); - _cascadedShadowMapHeightfieldProgram.link(); - - _cascadedShadowMapHeightfieldProgram.bind(); - _cascadedShadowMapHeightfieldProgram.setUniformValue("heightMap", 0); - _cascadedShadowMapHeightfieldProgram.setUniformValue("diffuseMap", 1); - _cascadedShadowMapHeightfieldProgram.setUniformValue("shadowMap", 2); - _cascadedShadowMapHeightScaleLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("heightScale"); - _cascadedShadowMapColorScaleLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("colorScale"); - _shadowDistancesLocation = _cascadedShadowMapHeightfieldProgram.uniformLocation("shadowDistances"); - _cascadedShadowMapHeightfieldProgram.release(); - _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_heightfield_base.vert"); _baseHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + @@ -1111,60 +1274,7 @@ void DefaultMetavoxelRendererImplementation::init() { _baseColorScaleLocation = _baseHeightfieldProgram.uniformLocation("colorScale"); _baseHeightfieldProgram.release(); - _splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_splat.vert"); - _splatHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_splat.frag"); - _splatHeightfieldProgram.link(); - - _splatHeightfieldProgram.bind(); - _splatHeightfieldProgram.setUniformValue("heightMap", 0); - _splatHeightfieldProgram.setUniformValue("textureMap", 1); - _splatHeightfieldProgram.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); - _splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale"); - _splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale"); - _splatTextureOffsetLocation = _splatHeightfieldProgram.uniformLocation("splatTextureOffset"); - _splatTextureScalesSLocation = _splatHeightfieldProgram.uniformLocation("splatTextureScalesS"); - _splatTextureScalesTLocation = _splatHeightfieldProgram.uniformLocation("splatTextureScalesT"); - _splatTextureValueMinimaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMinima"); - _splatTextureValueMaximaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMaxima"); - _splatHeightfieldProgram.release(); - - _lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light.vert"); - _lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light.frag"); - _lightHeightfieldProgram.link(); - - _lightHeightfieldProgram.bind(); - _lightHeightfieldProgram.setUniformValue("heightMap", 0); - _lightHeightScaleLocation = _lightHeightfieldProgram.uniformLocation("heightScale"); - _lightHeightfieldProgram.release(); - - _shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light.vert"); - _shadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light_shadow_map.frag"); - _shadowLightHeightfieldProgram.link(); - - _shadowLightHeightfieldProgram.bind(); - _shadowLightHeightfieldProgram.setUniformValue("heightMap", 0); - _shadowLightHeightfieldProgram.setUniformValue("shadowMap", 2); - _shadowLightHeightScaleLocation = _shadowLightHeightfieldProgram.uniformLocation("heightScale"); - _shadowLightHeightfieldProgram.release(); - - _cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light.vert"); - _cascadedShadowLightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/metavoxel_heightfield_light_cascaded_shadow_map.frag"); - _cascadedShadowLightHeightfieldProgram.link(); - - _cascadedShadowLightHeightfieldProgram.bind(); - _cascadedShadowLightHeightfieldProgram.setUniformValue("heightMap", 0); - _cascadedShadowLightHeightfieldProgram.setUniformValue("shadowMap", 2); - _cascadedShadowLightHeightScaleLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("heightScale"); - _shadowLightDistancesLocation = _cascadedShadowLightHeightfieldProgram.uniformLocation("shadowDistances"); - _cascadedShadowLightHeightfieldProgram.release(); + loadSplatProgram("heightfield", _splatHeightfieldProgram, _splatHeightfieldLocations); _heightfieldCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_heightfield_cursor.vert"); @@ -1175,6 +1285,14 @@ void DefaultMetavoxelRendererImplementation::init() { _heightfieldCursorProgram.bind(); _heightfieldCursorProgram.setUniformValue("heightMap", 0); _heightfieldCursorProgram.release(); + + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.vert"); + _baseVoxelProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_voxel_base.frag"); + _baseVoxelProgram.link(); + + loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations); } } @@ -1844,7 +1962,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { glm::vec3 center; glm::vec3 normal; const int MAX_MATERIALS_PER_VERTEX = 4; - quint8 materials[4]; + quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; float totalWeight = 0.0f; int red = 0, green = 0, blue = 0; @@ -1865,7 +1983,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { totalWeight += 1.0f; break; - } else if (materials[j] == 0.0f) { + } else if (materials[j] == 0) { materials[j] = crossing.material; materialWeights[j] = 1.0f; totalWeight += 1.0f; @@ -2149,38 +2267,22 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox _pointProgram.release(); + glDrawBuffers(sizeof(COLOR_NORMAL_DRAW_BUFFERS) / sizeof(COLOR_NORMAL_DRAW_BUFFERS[0]), COLOR_NORMAL_DRAW_BUFFERS); + glEnable(GL_CULL_FACE); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_EQUAL, 0.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - ProgramObject* program = &_heightfieldProgram; - if (Menu::getInstance()->getShadowsEnabled()) { - if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { - _cascadedShadowLightHeightfieldProgram.bind(); - _cascadedShadowLightHeightfieldProgram.setUniform(_shadowLightDistancesLocation, - Application::getInstance()->getShadowDistances()); - program = &_cascadedShadowMapHeightfieldProgram; - program->bind(); - program->setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances()); - - } else { - program = &_shadowMapHeightfieldProgram; - } - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - glActiveTexture(GL_TEXTURE0); - } - - program->bind(); + _baseHeightfieldProgram.bind(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); data.guide(heightfieldRenderVisitor); - program->release(); + _baseHeightfieldProgram.release(); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); @@ -2189,57 +2291,63 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - glEnable(GL_CULL_FACE); + _baseVoxelProgram.bind(); BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); + _baseVoxelProgram.release(); + + glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); + + glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS); +} + +void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, + ProgramObject& program, SplatLocations& locations) { + program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.vert"); + program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/metavoxel_" + type + "_splat.frag"); + program.link(); + + program.bind(); + program.setUniformValue("heightMap", 0); + program.setUniformValue("textureMap", 1); + program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); + locations.heightScale = program.uniformLocation("heightScale"); + locations.textureScale = program.uniformLocation("textureScale"); + locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); + locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); + locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); + locations.textureValueMinima = program.uniformLocation("textureValueMinima"); + locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); + locations.materials = program.attributeLocation("materials"); + locations.materialWeights = program.attributeLocation("materialWeights"); + program.release(); } ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; int DefaultMetavoxelRendererImplementation::_pointScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram; -int DefaultMetavoxelRendererImplementation::_heightScaleLocation; -int DefaultMetavoxelRendererImplementation::_colorScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_shadowMapHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_shadowMapHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_shadowMapColorScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_cascadedShadowMapHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_cascadedShadowMapColorScaleLocation; -int DefaultMetavoxelRendererImplementation::_shadowDistancesLocation; ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation; int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureOffsetLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureScalesSLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureScalesTLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureValueMinimaLocation; -int DefaultMetavoxelRendererImplementation::_splatTextureValueMaximaLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_shadowLightHeightScaleLocation; -ProgramObject DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightfieldProgram; -int DefaultMetavoxelRendererImplementation::_cascadedShadowLightHeightScaleLocation; -int DefaultMetavoxelRendererImplementation::_shadowLightDistancesLocation; +DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatHeightfieldLocations; ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram; +ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram; +ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram; +DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations; static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index aa15dcc8db..54bee3d451 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -52,6 +52,12 @@ public: Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID); + void noteNeedToLight() { _needToLight = true; } + +signals: + + void rendering(); + protected: virtual MetavoxelClient* createClient(const SharedNodePointer& node); @@ -60,6 +66,18 @@ private: void guideToAugmented(MetavoxelVisitor& visitor, bool render = false); + class LightLocations { + public: + int shadowDistances; + int shadowScale; + int near; + int depthScale; + int depthTexCoordOffset; + int depthTexCoordScale; + }; + + static void loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations); + AttributePointer _pointBufferAttribute; AttributePointer _heightfieldBufferAttribute; AttributePointer _voxelBufferAttribute; @@ -67,6 +85,14 @@ private: MetavoxelLOD _lod; QReadWriteLock _lodLock; Frustum _frustum; + bool _needToLight; + + ProgramObject _directionalLight; + LightLocations _directionalLightLocations; + ProgramObject _directionalLightShadowMap; + LightLocations _directionalLightShadowMapLocations; + ProgramObject _directionalLightCascadedShadowMap; + LightLocations _directionalLightCascadedShadowMapLocations; }; /// Describes contents of a point in a point buffer. @@ -266,42 +292,33 @@ public: static void init(); - static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; } - static int getHeightScaleLocation() { return _heightScaleLocation; } - static int getColorScaleLocation() { return _colorScaleLocation; } - - static ProgramObject& getShadowMapHeightfieldProgram() { return _shadowMapHeightfieldProgram; } - static int getShadowMapHeightScaleLocation() { return _shadowMapHeightScaleLocation; } - static int getShadowMapColorScaleLocation() { return _shadowMapColorScaleLocation; } - - static ProgramObject& getCascadedShadowMapHeightfieldProgram() { return _cascadedShadowMapHeightfieldProgram; } - static int getCascadedShadowMapHeightScaleLocation() { return _cascadedShadowMapHeightScaleLocation; } - static int getCascadedShadowMapColorScaleLocation() { return _cascadedShadowMapColorScaleLocation; } - static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; } static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; } static int getBaseColorScaleLocation() { return _baseColorScaleLocation; } + class SplatLocations { + public: + int heightScale; + int textureScale; + int splatTextureOffset; + int splatTextureScalesS; + int splatTextureScalesT; + int textureValueMinima; + int textureValueMaxima; + int materials; + int materialWeights; + }; + static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; } - static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; } - static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; } - static int getSplatTextureOffsetLocation() { return _splatTextureOffsetLocation; } - static int getSplatTextureScalesSLocation() { return _splatTextureScalesSLocation; } - static int getSplatTextureScalesTLocation() { return _splatTextureScalesTLocation; } - static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; } - static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; } - - static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; } - static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; } - - static ProgramObject& getShadowLightHeightfieldProgram() { return _shadowLightHeightfieldProgram; } - static int getShadowLightHeightScaleLocation() { return _shadowLightHeightScaleLocation; } - - static ProgramObject& getCascadedShadowLightHeightfieldProgram() { return _cascadedShadowLightHeightfieldProgram; } - static int getCascadedShadowLightHeightScaleLocation() { return _cascadedShadowLightHeightScaleLocation; } + static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; } static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; } + static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; } + + static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; } + static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; } + Q_INVOKABLE DefaultMetavoxelRendererImplementation(); virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod); @@ -310,27 +327,18 @@ public: private: + static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations); + static ProgramObject _pointProgram; static int _pointScaleLocation; - static ProgramObject _heightfieldProgram; - static int _heightScaleLocation; - static int _colorScaleLocation; - - static ProgramObject _shadowMapHeightfieldProgram; - static int _shadowMapHeightScaleLocation; - static int _shadowMapColorScaleLocation; - - static ProgramObject _cascadedShadowMapHeightfieldProgram; - static int _cascadedShadowMapHeightScaleLocation; - static int _cascadedShadowMapColorScaleLocation; - static int _shadowDistancesLocation; - static ProgramObject _baseHeightfieldProgram; static int _baseHeightScaleLocation; static int _baseColorScaleLocation; static ProgramObject _splatHeightfieldProgram; + static SplatLocations _splatHeightfieldLocations; + static int _splatHeightScaleLocation; static int _splatTextureScaleLocation; static int _splatTextureOffsetLocation; @@ -339,17 +347,11 @@ private: static int _splatTextureValueMinimaLocation; static int _splatTextureValueMaximaLocation; - static ProgramObject _lightHeightfieldProgram; - static int _lightHeightScaleLocation; - - static ProgramObject _shadowLightHeightfieldProgram; - static int _shadowLightHeightScaleLocation; - - static ProgramObject _cascadedShadowLightHeightfieldProgram; - static int _cascadedShadowLightHeightScaleLocation; - static int _shadowLightDistancesLocation; - static ProgramObject _heightfieldCursorProgram; + + static ProgramObject _baseVoxelProgram; + static ProgramObject _splatVoxelProgram; + static SplatLocations _splatVoxelLocations; }; /// Base class for spanner renderers; provides clipping. diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index d960525817..bb07e83980 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -29,6 +29,7 @@ TextureCache::TextureCache() : _whiteTextureID(0), _blueTextureID(0), _primaryDepthTextureID(0), + _primaryNormalTextureID(0), _primaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL), _tertiaryFramebufferObject(NULL), @@ -46,6 +47,7 @@ TextureCache::~TextureCache() { } if (_primaryFramebufferObject) { glDeleteTextures(1, &_primaryDepthTextureID); + glDeleteTextures(1, &_primaryNormalTextureID); } if (_primaryFramebufferObject) { @@ -71,6 +73,8 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) { _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); _primaryDepthTextureID = 0; + glDeleteTextures(1, &_primaryNormalTextureID); + _primaryNormalTextureID = 0; } if (_secondaryFramebufferObject) { @@ -205,15 +209,22 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() { glGenTextures(1, &_primaryDepthTextureID); glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glGenTextures(1, &_primaryNormalTextureID); + glBindTexture(GL_TEXTURE_2D, _primaryNormalTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); _primaryFramebufferObject->bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _primaryNormalTextureID, 0); _primaryFramebufferObject->release(); } return _primaryFramebufferObject; @@ -225,6 +236,12 @@ GLuint TextureCache::getPrimaryDepthTextureID() { return _primaryDepthTextureID; } +GLuint TextureCache::getPrimaryNormalTextureID() { + // ensure that the primary framebuffer object is initialized before returning the normal texture id + getPrimaryFramebufferObject(); + return _primaryNormalTextureID; +} + QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() { if (!_secondaryFramebufferObject) { _secondaryFramebufferObject = createFramebufferObject(); @@ -278,6 +295,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) { delete _primaryFramebufferObject; _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); + glDeleteTextures(1, &_primaryNormalTextureID); } if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) { delete _secondaryFramebufferObject; diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index e1d69677f6..ce0e7661af 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -61,6 +61,9 @@ public: /// Returns the ID of the primary framebuffer object's depth texture. This contains the Z buffer used in rendering. GLuint getPrimaryDepthTextureID(); + /// Returns the ID of the primary framebuffer object's normal texture. + GLuint getPrimaryNormalTextureID(); + /// Returns a pointer to the secondary framebuffer object, used as an additional render target when performing full /// screen effects. QOpenGLFramebufferObject* getSecondaryFramebufferObject(); @@ -95,6 +98,7 @@ private: QHash > _dilatableNetworkTextures; GLuint _primaryDepthTextureID; + GLuint _primaryNormalTextureID; QOpenGLFramebufferObject* _primaryFramebufferObject; QOpenGLFramebufferObject* _secondaryFramebufferObject; QOpenGLFramebufferObject* _tertiaryFramebufferObject; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b9e262f3fc..dc669365e8 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -971,11 +971,9 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) : connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile); _form->addRow("Color:", _color = new QPushButton()); connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile); -} - -void ImportHeightfieldTool::render() { - HeightfieldTool::render(); - _preview.render(_translation->getValue(), _translation->getSingleStep()); + + connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering, + this, &ImportHeightfieldTool::renderPreview); } void ImportHeightfieldTool::apply() { @@ -1084,6 +1082,12 @@ void ImportHeightfieldTool::updatePreview() { _preview.setBuffers(buffers); } +void ImportHeightfieldTool::renderPreview() { + if (isVisible()) { + _preview.render(_translation->getValue(), _translation->getSingleStep()); + } +} + EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) : HeightfieldTool(editor, "Erase Heightfield") { diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index 96f4a2abe9..447d2197cd 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -283,8 +283,6 @@ public: ImportHeightfieldTool(MetavoxelEditor* editor); - virtual void render(); - protected: virtual void apply(); @@ -294,6 +292,7 @@ private slots: void selectHeightFile(); void selectColorFile(); void updatePreview(); + void renderPreview(); private: From 137d3738fc2bc66ceecccb921742f909dfae1ba0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Sep 2014 17:23:09 -0700 Subject: [PATCH 44/51] Fix for preview render. --- interface/src/MetavoxelSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d3819bf89b..bb574f77bc 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1073,6 +1073,8 @@ void HeightfieldBuffer::render(bool cursor) { QHash HeightfieldBuffer::_bufferPairs; void HeightfieldPreview::render(const glm::vec3& translation, float scale) const { + glDrawBuffers(sizeof(COLOR_NORMAL_DRAW_BUFFERS) / sizeof(COLOR_NORMAL_DRAW_BUFFERS[0]), COLOR_NORMAL_DRAW_BUFFERS); + glDisable(GL_BLEND); glEnable(GL_CULL_FACE); glEnable(GL_ALPHA_TEST); @@ -1103,6 +1105,8 @@ void HeightfieldPreview::render(const glm::vec3& translation, float scale) const glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); + + glDrawBuffers(sizeof(COLOR_DRAW_BUFFERS) / sizeof(COLOR_DRAW_BUFFERS[0]), COLOR_DRAW_BUFFERS); } VoxelBuffer::VoxelBuffer(const QVector& vertices, const QVector& indices, From 83d209dbdcb385f60e31980dac47786802b3abde Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Sep 2014 17:26:01 -0700 Subject: [PATCH 45/51] Remove unused variables. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index fcf53c1106..79ac74a1f6 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -639,7 +639,6 @@ public: private: Box _region; - float _granularity; SharedObjectPointer _material; QColor _color; float _blockSize; @@ -654,7 +653,6 @@ VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const Box& region, floa AttributeRegistry::getInstance()->getVoxelHermiteAttribute() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute()), _region(region), - _granularity(granularity), _material(material), _color(color), _blockSize(granularity * VOXEL_BLOCK_SIZE) { @@ -836,7 +834,6 @@ private: glm::vec3 _center; float _radius; Box _bounds; - float _granularity; SharedObjectPointer _material; QColor _color; float _blockSize; @@ -853,7 +850,6 @@ VoxelMaterialSphereEditVisitor::VoxelMaterialSphereEditVisitor(const glm::vec3& _center(center), _radius(radius), _bounds(bounds), - _granularity(granularity), _material(material), _color(color), _blockSize(granularity * VOXEL_BLOCK_SIZE) { From c340445f33c9342d78a123b57eda481da337e736 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 4 Sep 2014 17:42:33 -0700 Subject: [PATCH 46/51] Hopefully, a fix for the Windows build; looks like "near" is a keyword. --- interface/src/MetavoxelSystem.cpp | 4 ++-- interface/src/MetavoxelSystem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index bb574f77bc..892efe7211 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -207,7 +207,7 @@ void MetavoxelSystem::render() { glm::vec4 nearClipPlane, farClipPlane; Application::getInstance()->computeOffAxisFrustum( left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - program->setUniformValue(locations->near, nearVal); + program->setUniformValue(locations->nearLocation, nearVal); program->setUniformValue(locations->depthScale, (farVal - nearVal) / farVal); float nearScale = -1.0f / nearVal; program->setUniformValue(locations->depthTexCoordOffset, left * nearScale, bottom * nearScale); @@ -630,7 +630,7 @@ void MetavoxelSystem::loadLightProgram(const char* name, ProgramObject& program, program.setUniformValue("shadowMap", 3); locations.shadowDistances = program.uniformLocation("shadowDistances"); locations.shadowScale = program.uniformLocation("shadowScale"); - locations.near = program.uniformLocation("near"); + locations.nearLocation = program.uniformLocation("near"); locations.depthScale = program.uniformLocation("depthScale"); locations.depthTexCoordOffset = program.uniformLocation("depthTexCoordOffset"); locations.depthTexCoordScale = program.uniformLocation("depthTexCoordScale"); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 54bee3d451..f3b9a02117 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -70,7 +70,7 @@ private: public: int shadowDistances; int shadowScale; - int near; + int nearLocation; int depthScale; int depthTexCoordOffset; int depthTexCoordScale; From fe0b9825d0c51646c17d3a9d65e0b8da511111eb Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 4 Sep 2014 18:10:29 -0700 Subject: [PATCH 47/51] new coordinate calculations for head penumbra filter --- assignment-client/src/audio/AudioMixer.cpp | 65 ++++++++++++++-------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 51ef47b67d..89834a8c9e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -141,7 +141,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* float weakChannelAmplitudeRatio = 1.0f; bool shouldAttenuate = (streamToAdd != listeningNodeStream); - + if (shouldAttenuate) { // if the two stream pointers do not match then these are different streams @@ -306,40 +306,61 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* } if (_enableFilter && shouldAttenuate) { - + glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream->getPosition(); - if (relativePosition.z < 0) { // if the source is behind us + glm::quat inverseOrientation = glm::inverse(listeningNodeStream->getOrientation()); + glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; + + // project the rotated source position vector onto the XZ plane + rotatedSourcePosition.y = 0.0f; + + // produce an oriented angle about the y-axis + bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), + glm::normalize(rotatedSourcePosition), + glm::vec3(0.0f, 1.0f, 0.0f)); + + // if the source is in the range [-pi/2,+pi/2] (e.g, -Z from the listener's perspective + if (bearingRelativeAngleToSource <= -PI_OVER_TWO || bearingRelativeAngleToSource >= PI_OVER_TWO) + { AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter(); - // calculate penumbra angle - float headPenumbraAngle = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), - glm::normalize(relativePosition)); + const float FULL_POWER = 1.0f; + const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f; + const float HALF_POWER = SQUARE_ROOT_OF_TWO_OVER_TWO; + const float QUARTER_POWER = /*HALF_POWER * */HALF_POWER; - if (relativePosition.x < 0) { - headPenumbraAngle *= -1.0f; // [-pi/2,+pi/2] - } - - const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f; // half power const float ONE_OVER_TWO_PI = 1.0f / TWO_PI; - const float FILTER_CUTOFF_FREQUENCY_HZ = 4000.0f; + const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; - // calculate the updated gain, frequency and slope. this will be tuned over time. - const float penumbraFilterGainL = (-1.0f * ONE_OVER_TWO_PI * headPenumbraAngle) + SQUARE_ROOT_OF_TWO_OVER_TWO; - const float penumbraFilterGainR = (+1.0f * ONE_OVER_TWO_PI * headPenumbraAngle) + SQUARE_ROOT_OF_TWO_OVER_TWO; + // calculate the updated gain, frequency and slope. const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency const float penumbraFilterSlope = SQUARE_ROOT_OF_TWO_OVER_TWO; // constant slope + + const float penumbraFilterGainL = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ? + ((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + FULL_POWER) : + ((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); + + const float penumbraFilterGainR = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ? + ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + QUARTER_POWER) : + ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); - qDebug() << "penumbra gainL=" - << penumbraFilterGainL - << "penumbra gainR=" - << penumbraFilterGainR - << "penumbraAngle=" - << headPenumbraAngle; + float distanceBetween = glm::length(relativePosition); + + qDebug() << "avatar=" + << listeningNodeStream + << "gainL=" + << penumbraFilterGainL + << "gainR=" + << penumbraFilterGainR + << "angle=" + << bearingRelativeAngleToSource + << "dist=" + << distanceBetween; // set the gain on both filter channels penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); - + penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); } } From fc0a20698584a3322f16ec7b009d19f257b2be98 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Thu, 4 Sep 2014 22:57:02 -0700 Subject: [PATCH 48/51] code review comments --- assignment-client/src/audio/AudioMixer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 89834a8c9e..3f5829b72f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -319,15 +319,15 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, 1.0f, 0.0f)); - // if the source is in the range [-pi/2,+pi/2] (e.g, -Z from the listener's perspective - if (bearingRelativeAngleToSource <= -PI_OVER_TWO || bearingRelativeAngleToSource >= PI_OVER_TWO) + // if the source is in the range (-pi/2,+pi/2) (e.g, -Z from the listener's perspective + if (bearingRelativeAngleToSource < -PI_OVER_TWO || bearingRelativeAngleToSource > PI_OVER_TWO) { AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter(); const float FULL_POWER = 1.0f; const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f; const float HALF_POWER = SQUARE_ROOT_OF_TWO_OVER_TWO; - const float QUARTER_POWER = /*HALF_POWER * */HALF_POWER; + const float QUARTER_POWER = HALF_POWER * HALF_POWER; const float ONE_OVER_TWO_PI = 1.0f / TWO_PI; const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; @@ -341,11 +341,11 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* ((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); const float penumbraFilterGainR = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ? - ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + QUARTER_POWER) : + ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + HALF_POWER) : ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); float distanceBetween = glm::length(relativePosition); - +#if 0 qDebug() << "avatar=" << listeningNodeStream << "gainL=" @@ -356,7 +356,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* << bearingRelativeAngleToSource << "dist=" << distanceBetween; - +#endif // set the gain on both filter channels penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); From a603453cf21922cdb3c01b6352bdb43979968e5a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 5 Sep 2014 11:36:56 -0600 Subject: [PATCH 49/51] Revert "Update assignment client to close on WM_CLOSE message on Windows" --- assignment-client/src/AssignmentClient.cpp | 9 +----- assignment-client/src/AssignmentClient.h | 3 -- domain-server/src/DomainServer.cpp | 8 ----- domain-server/src/DomainServer.h | 4 --- libraries/shared/src/LogUtils.cpp | 24 -------------- libraries/shared/src/LogUtils.h | 20 ------------ .../shared/src/ShutdownEventListener.cpp | 31 ------------------- libraries/shared/src/ShutdownEventListener.h | 29 ----------------- 8 files changed, 1 insertion(+), 127 deletions(-) delete mode 100644 libraries/shared/src/LogUtils.cpp delete mode 100644 libraries/shared/src/LogUtils.h delete mode 100644 libraries/shared/src/ShutdownEventListener.cpp delete mode 100644 libraries/shared/src/ShutdownEventListener.h diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 3e3c7c0609..f7594e97f2 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -37,19 +36,13 @@ int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), - _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME), - _shutdownEventListener(this) + _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) { - LogUtils::init(); - setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); QSettings::setDefaultFormat(QSettings::IniFormat); - installNativeEventFilter(&_shutdownEventListener); - connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit())); - // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 7628aa0a3b..9834402dbf 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -14,7 +14,6 @@ #include -#include "ShutdownEventListener.h" #include "ThreadedAssignment.h" class AssignmentClient : public QCoreApplication { @@ -22,7 +21,6 @@ class AssignmentClient : public QCoreApplication { public: AssignmentClient(int &argc, char **argv); static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } - private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -32,7 +30,6 @@ private slots: private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; - ShutdownEventListener _shutdownEventListener; QString _assignmentServerHostname; }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 56570a4b82..2cd8150e63 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), - _shutdownEventListener(this), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpsManager(NULL), _allAssignments(), @@ -48,16 +46,10 @@ DomainServer::DomainServer(int argc, char* argv[]) : _cookieSessionHash(), _settingsManager() { - - LogUtils::init(); - setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); QSettings::setDefaultFormat(QSettings::IniFormat); - - installNativeEventFilter(&_shutdownEventListener); - connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit())); qRegisterMetaType("DomainServerWebSessionData"); qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 10633cc71a..666994c818 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -27,7 +26,6 @@ #include "DomainServerSettingsManager.h" #include "DomainServerWebSessionData.h" -#include "ShutdownEventListener.h" #include "WalletTransaction.h" #include "PendingAssignedNodeData.h" @@ -99,8 +97,6 @@ private: QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); - - ShutdownEventListener _shutdownEventListener; HTTPManager _httpManager; HTTPSManager* _httpsManager; diff --git a/libraries/shared/src/LogUtils.cpp b/libraries/shared/src/LogUtils.cpp deleted file mode 100644 index c38f5c8574..0000000000 --- a/libraries/shared/src/LogUtils.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// LogUtils.cpp -// libraries/shared/src -// -// Created by Ryan Huffman on 09/03/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "LogUtils.h" - -void LogUtils::init() { -#ifdef Q_OS_WIN - // Windows applications buffer stdout/err hard when not run from a terminal, - // making assignment clients run from the Stack Manager application not flush - // log messages. - // This will disable the buffering. If this becomes a performance issue, - // an alternative is to call fflush(...) periodically. - setbuf(stdout, NULL); - setbuf(stderr, NULL); -#endif -} diff --git a/libraries/shared/src/LogUtils.h b/libraries/shared/src/LogUtils.h deleted file mode 100644 index 955c33d338..0000000000 --- a/libraries/shared/src/LogUtils.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// LogUtils.h -// libraries/shared/src -// -// Created by Ryan Huffman on 09/03/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_LogUtils_h -#define hifi_LogUtils_h - -class LogUtils { -public: - static void init(); -}; - -#endif // hifi_LogUtils_h diff --git a/libraries/shared/src/ShutdownEventListener.cpp b/libraries/shared/src/ShutdownEventListener.cpp deleted file mode 100644 index 961d18e793..0000000000 --- a/libraries/shared/src/ShutdownEventListener.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// ShutdownEventListener.cpp -// libraries/shared/src -// -// Created by Ryan Huffman on 09/03/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ShutdownEventListener.h" - -#ifdef Q_OS_WIN -#include -#endif - -ShutdownEventListener::ShutdownEventListener(QObject* parent) : QObject(parent) { -} - -bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) { -#ifdef Q_OS_WIN - if (eventType == "windows_generic_MSG") { - MSG* message = (MSG*)msg; - if (message->message == WM_CLOSE) { - emit receivedCloseEvent(); - } - } -#endif - return true; -} diff --git a/libraries/shared/src/ShutdownEventListener.h b/libraries/shared/src/ShutdownEventListener.h deleted file mode 100644 index e39770c13d..0000000000 --- a/libraries/shared/src/ShutdownEventListener.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// ShutdownEventListener.h -// libraries/shared/src -// -// Created by Ryan Huffman on 09/03/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ShutdownEventListener_h -#define hifi_ShutdownEventListener_h - -#include -#include - -class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter { - Q_OBJECT -public: - ShutdownEventListener(QObject* parent = NULL); - - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result); - -signals: - void receivedCloseEvent(); -}; - -#endif // hifi_ShutdownEventListener_h From 38a14c3864dd5ecbb8e5bc3520c9e196b1b02777 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 5 Sep 2014 11:19:36 -0700 Subject: [PATCH 50/51] Reapply "Update assignment client to close on WM_CLOSE message on Windows" This reverts commit a603453cf21922cdb3c01b6352bdb43979968e5a. --- assignment-client/src/AssignmentClient.cpp | 9 +++++- assignment-client/src/AssignmentClient.h | 3 ++ domain-server/src/DomainServer.cpp | 8 +++++ domain-server/src/DomainServer.h | 4 +++ libraries/shared/src/LogUtils.cpp | 24 ++++++++++++++ libraries/shared/src/LogUtils.h | 20 ++++++++++++ .../shared/src/ShutdownEventListener.cpp | 31 +++++++++++++++++++ libraries/shared/src/ShutdownEventListener.h | 29 +++++++++++++++++ 8 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 libraries/shared/src/LogUtils.cpp create mode 100644 libraries/shared/src/LogUtils.h create mode 100644 libraries/shared/src/ShutdownEventListener.cpp create mode 100644 libraries/shared/src/ShutdownEventListener.h diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index f7594e97f2..3e3c7c0609 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -36,13 +37,19 @@ int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), - _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) + _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME), + _shutdownEventListener(this) { + LogUtils::init(); + setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); QSettings::setDefaultFormat(QSettings::IniFormat); + installNativeEventFilter(&_shutdownEventListener); + connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit())); + // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 9834402dbf..7628aa0a3b 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -14,6 +14,7 @@ #include +#include "ShutdownEventListener.h" #include "ThreadedAssignment.h" class AssignmentClient : public QCoreApplication { @@ -21,6 +22,7 @@ class AssignmentClient : public QCoreApplication { public: AssignmentClient(int &argc, char **argv); static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } + private slots: void sendAssignmentRequest(); void readPendingDatagrams(); @@ -30,6 +32,7 @@ private slots: private: Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; + ShutdownEventListener _shutdownEventListener; QString _assignmentServerHostname; }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2cd8150e63..56570a4b82 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), + _shutdownEventListener(this), _httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpsManager(NULL), _allAssignments(), @@ -46,10 +48,16 @@ DomainServer::DomainServer(int argc, char* argv[]) : _cookieSessionHash(), _settingsManager() { + + LogUtils::init(); + setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); QSettings::setDefaultFormat(QSettings::IniFormat); + + installNativeEventFilter(&_shutdownEventListener); + connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit())); qRegisterMetaType("DomainServerWebSessionData"); qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 666994c818..10633cc71a 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #include "DomainServerSettingsManager.h" #include "DomainServerWebSessionData.h" +#include "ShutdownEventListener.h" #include "WalletTransaction.h" #include "PendingAssignedNodeData.h" @@ -97,6 +99,8 @@ private: QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); + + ShutdownEventListener _shutdownEventListener; HTTPManager _httpManager; HTTPSManager* _httpsManager; diff --git a/libraries/shared/src/LogUtils.cpp b/libraries/shared/src/LogUtils.cpp new file mode 100644 index 0000000000..c38f5c8574 --- /dev/null +++ b/libraries/shared/src/LogUtils.cpp @@ -0,0 +1,24 @@ +// +// LogUtils.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 09/03/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "LogUtils.h" + +void LogUtils::init() { +#ifdef Q_OS_WIN + // Windows applications buffer stdout/err hard when not run from a terminal, + // making assignment clients run from the Stack Manager application not flush + // log messages. + // This will disable the buffering. If this becomes a performance issue, + // an alternative is to call fflush(...) periodically. + setbuf(stdout, NULL); + setbuf(stderr, NULL); +#endif +} diff --git a/libraries/shared/src/LogUtils.h b/libraries/shared/src/LogUtils.h new file mode 100644 index 0000000000..955c33d338 --- /dev/null +++ b/libraries/shared/src/LogUtils.h @@ -0,0 +1,20 @@ +// +// LogUtils.h +// libraries/shared/src +// +// Created by Ryan Huffman on 09/03/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_LogUtils_h +#define hifi_LogUtils_h + +class LogUtils { +public: + static void init(); +}; + +#endif // hifi_LogUtils_h diff --git a/libraries/shared/src/ShutdownEventListener.cpp b/libraries/shared/src/ShutdownEventListener.cpp new file mode 100644 index 0000000000..961d18e793 --- /dev/null +++ b/libraries/shared/src/ShutdownEventListener.cpp @@ -0,0 +1,31 @@ +// +// ShutdownEventListener.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 09/03/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ShutdownEventListener.h" + +#ifdef Q_OS_WIN +#include +#endif + +ShutdownEventListener::ShutdownEventListener(QObject* parent) : QObject(parent) { +} + +bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) { +#ifdef Q_OS_WIN + if (eventType == "windows_generic_MSG") { + MSG* message = (MSG*)msg; + if (message->message == WM_CLOSE) { + emit receivedCloseEvent(); + } + } +#endif + return true; +} diff --git a/libraries/shared/src/ShutdownEventListener.h b/libraries/shared/src/ShutdownEventListener.h new file mode 100644 index 0000000000..e39770c13d --- /dev/null +++ b/libraries/shared/src/ShutdownEventListener.h @@ -0,0 +1,29 @@ +// +// ShutdownEventListener.h +// libraries/shared/src +// +// Created by Ryan Huffman on 09/03/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ShutdownEventListener_h +#define hifi_ShutdownEventListener_h + +#include +#include + +class ShutdownEventListener : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT +public: + ShutdownEventListener(QObject* parent = NULL); + + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result); + +signals: + void receivedCloseEvent(); +}; + +#endif // hifi_ShutdownEventListener_h From b0a0760be255c5a574cf573caba449ce6477d8bc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 5 Sep 2014 11:26:22 -0700 Subject: [PATCH 51/51] Update ShutdownEventListener to not stop event handling --- libraries/shared/src/ShutdownEventListener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/ShutdownEventListener.cpp b/libraries/shared/src/ShutdownEventListener.cpp index 961d18e793..6cd7592b69 100644 --- a/libraries/shared/src/ShutdownEventListener.cpp +++ b/libraries/shared/src/ShutdownEventListener.cpp @@ -27,5 +27,5 @@ bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void* } } #endif - return true; + return false; }