From 32781d53a2cf2dd7efd5802fd6b48871f5caf1d4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Oct 2014 17:35:37 -0700 Subject: [PATCH 01/17] Working on allowing different resolutions for heightfield heights and colors. --- interface/src/ui/MetavoxelEditor.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 8c5080c6f8..3568c15eb2 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1143,7 +1143,9 @@ void ImportHeightfieldTool::updatePreview() { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE; + int colorScale = glm::round(glm::log(_colorImage.height() / _heightImage.height()) / glm::log(2.0f)); + int colorBlockSize = blockSize * pow(2.0, colorScale); + int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { @@ -1164,12 +1166,14 @@ void ImportHeightfieldTool::updatePreview() { } QByteArray color; if (!_colorImage.isNull()) { + int colorI = (i / blockSize) * colorBlockSize; + int colorJ = (j / blockSize) * colorBlockSize; color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0); - rows = qMax(0, qMin(colorSize, _colorImage.height() - i)); - columns = qMax(0, qMin(colorSize, _colorImage.width() - j)); + rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI)); + columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ)); for (int y = 0; y < rows; y++) { memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES, - _colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES, + _colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES, columns * DataBlock::COLOR_BYTES); } } From d28da9250f218bcaf9dd6ce5d74d0cfa6922f81e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Oct 2014 18:31:34 -0700 Subject: [PATCH 02/17] More progress towards allowing different resolutions for height and color, material, etc. --- interface/src/MetavoxelSystem.cpp | 357 +++++++++++------- interface/src/MetavoxelSystem.h | 2 +- interface/src/ui/MetavoxelEditor.cpp | 54 ++- .../metavoxels/src/AttributeRegistry.cpp | 12 + libraries/metavoxels/src/AttributeRegistry.h | 6 + 5 files changed, 281 insertions(+), 150 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 20e05e779e..b9baa356f5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1006,16 +1006,14 @@ QByteArray HeightfieldBuffer::getUnextendedHeight() const { return unextended; } -QByteArray HeightfieldBuffer::getUnextendedColor() const { - int srcSize = glm::sqrt(float(_color.size() / DataBlock::COLOR_BYTES)); - int destSize = srcSize - 1; - QByteArray unextended(destSize * destSize * DataBlock::COLOR_BYTES, 0); - const char* src = _color.constData(); - int srcStride = srcSize * DataBlock::COLOR_BYTES; +QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const { + int unextendedSize = _heightSize - HEIGHT_EXTENSION; + QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0); char* dest = unextended.data(); - int destStride = destSize * DataBlock::COLOR_BYTES; - for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) { - memcpy(dest, src, destStride); + const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES; + for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES, + src += _colorSize * DataBlock::COLOR_BYTES) { + memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES); } return unextended; } @@ -1702,134 +1700,170 @@ public: HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections); - void init(HeightfieldBuffer* buffer) { _buffer = buffer; } + void init(HeightfieldBuffer* buffer); virtual int visit(MetavoxelInfo& info); + virtual bool postVisit(MetavoxelInfo& info); private: const QVector& _intersections; HeightfieldBuffer* _buffer; + + int _heightDepth; + int _colorDepth; + int _materialDepth; }; HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), + AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector(), lod), _intersections(intersections) { } +void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { + _buffer = buffer; + _heightDepth = -1; + _colorDepth = -1; + _materialDepth = -1; +} + int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - const Box& heightBounds = _buffer->getHeightBounds(); - if (!bounds.intersects(heightBounds)) { + if (!info.getBounds().intersects(_buffer->getHeightBounds())) { return STOP_RECURSION; } - if (!info.isLeaf && info.size > _buffer->getScale()) { + if (!info.isLeaf) { return DEFAULT_ORDER; } + postVisit(info); + return STOP_RECURSION; +} + +bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (!height) { - return STOP_RECURSION; + if (height || _heightDepth != -1) { + if (_depth < _heightDepth) { + height.reset(); + } + _heightDepth = _depth; } + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); + if (color || _colorDepth != -1) { + if (_depth < _colorDepth) { + color.reset(); + } + _colorDepth = _depth; + } + HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); + if (material || _materialDepth != -1) { + if (_depth < _materialDepth) { + material.reset(); + } + _materialDepth = _depth; + } + if (!(height || color || material)) { + return false; + } + Box bounds = info.getBounds(); foreach (const Box& intersection, _intersections) { Box overlap = intersection.getIntersection(bounds); if (overlap.isEmpty()) { continue; } - float heightIncrement = _buffer->getHeightIncrement(); - int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; - int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); - int heightSize = _buffer->getHeightSize(); - char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - - const QByteArray& srcHeight = height->getContents(); - int srcSize = glm::sqrt(float(srcHeight.size())); - float srcIncrement = info.size / srcSize; - - if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + if (height) { + float heightIncrement = _buffer->getHeightIncrement(); + const Box& heightBounds = _buffer->getHeightBounds(); + int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; + int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); + int heightSize = _buffer->getHeightSize(); + char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - const char* src = srcHeight.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { - memcpy(dest, src, destWidth); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = heightIncrement / srcIncrement; - int shift = 0; - float size = _buffer->getScale(); - while (size < info.size) { - shift++; - size *= 2.0f; - } - 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; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + const QByteArray& srcHeight = height->getContents(); + int srcSize = glm::sqrt((float)srcHeight.size()); + float srcIncrement = info.size / srcSize; + + if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const char* src = srcHeight.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { + memcpy(dest, src, destWidth); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = heightIncrement / srcIncrement; + int shift = 0; + float size = _buffer->getScale(); + while (size < info.size) { + shift++; + size *= 2.0f; + } + 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; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + } } } } - - int colorSize = _buffer->getColorSize(); - if (colorSize == 0) { - continue; - } - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (!color) { - continue; - } - const Box& colorBounds = _buffer->getColorBounds(); - overlap = colorBounds.getIntersection(overlap); - float colorIncrement = _buffer->getColorIncrement(); - destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; - 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) * DataBlock::COLOR_BYTES; - int destStride = colorSize * DataBlock::COLOR_BYTES; - int destBytes = destWidth * DataBlock::COLOR_BYTES; - - const QByteArray& srcColor = color->getContents(); - srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); - int srcStride = srcSize * DataBlock::COLOR_BYTES; - srcIncrement = info.size / srcSize; - - if (srcIncrement == colorIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + if (color) { + const Box& colorBounds = _buffer->getColorBounds(); + overlap = colorBounds.getIntersection(overlap); + float colorIncrement = _buffer->getColorIncrement(); + int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; + int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); + int colorSize = _buffer->getColorSize(); + char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; + int destStride = colorSize * DataBlock::COLOR_BYTES; + int destBytes = destWidth * DataBlock::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); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = colorIncrement / srcIncrement; - 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 += DataBlock::COLOR_BYTES, - lineSrcX += srcAdvance) { - const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; + const QByteArray& srcColor = color->getContents(); + int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); + int srcStride = srcSize * DataBlock::COLOR_BYTES; + float srcIncrement = info.size / srcSize; + + if (srcIncrement == colorIncrement) { + // easy case: same resolution + 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) * DataBlock::COLOR_BYTES; + for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, destBytes); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = colorIncrement / srcIncrement; + 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 += DataBlock::COLOR_BYTES, + lineSrcX += srcAdvance) { + const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; + lineDest[0] = lineSrc[0]; + lineDest[1] = lineSrc[1]; + lineDest[2] = lineSrc[2]; + } } } } + if (material) { + } } - return STOP_RECURSION; + return false; } class HeightfieldRegionVisitor : public MetavoxelVisitor { @@ -1841,11 +1875,27 @@ public: HeightfieldRegionVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); + virtual bool postVisit(MetavoxelInfo& info); private: void addRegion(const Box& unextended, const Box& extended); + int _heightSize; + int _heightDepth; + + int _inheritedColorSize; + int _inheritedColorDepth; + + int _containedColorSize; + int _containedColorDepth; + + int _inheritedMaterialSize; + int _inheritedMaterialDepth; + + int _containedMaterialSize; + int _containedMaterialDepth; + QVector _intersections; HeightfieldFetchVisitor _fetchVisitor; }; @@ -1857,52 +1907,92 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : 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)), + _heightDepth(-1), + _inheritedColorDepth(-1), + _containedColorDepth(-1), + _inheritedMaterialDepth(-1), + _containedMaterialDepth(-1), _fetchVisitor(lod, _intersections) { } +static int signedLeftShift(int value, int shift) { + return (shift > 0) ? value << shift : value >> (-shift); +} + int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldBuffer* buffer = NULL; HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + int order = DEFAULT_ORDER; if (height) { - const QByteArray& heightContents = height->getContents(); - int size = glm::sqrt(float(heightContents.size())); - int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedSize * extendedSize; - - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - int colorContentsSize = 0; - if (color) { - const QByteArray& colorContents = color->getContents(); - int colorSize = glm::sqrt(float(colorContents.size() / DataBlock::COLOR_BYTES)); - int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; - colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; + _heightSize = glm::sqrt((float)height->getContents().size()); + _heightDepth = _depth; + order |= ALL_NODES_REST; + } + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); + if (color) { + int colorSize = glm::sqrt((float)(color->getContents().size() / DataBlock::COLOR_BYTES)); + if (_heightDepth == -1) { + _inheritedColorSize = colorSize; + _inheritedColorDepth = _depth; + + } else if (_containedColorDepth == -1 || + colorSize > signedLeftShift(_containedColorSize, _containedColorDepth - _depth)) { + _containedColorSize = colorSize; + _containedColorDepth = _depth; } + } + HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); + if (material) { + int materialSize = glm::sqrt((float)material->getContents().size()); + if (_heightDepth == -1) { + _inheritedMaterialSize = materialSize; + _inheritedMaterialDepth = _depth; - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - QByteArray materialContents; - QVector materials; - if (material) { - materialContents = material->getContents(); - materials = material->getMaterials(); + } else if (_containedMaterialDepth == -1 || + materialSize > signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth)) { + _containedMaterialSize = materialSize; + _containedMaterialDepth = _depth; } + } + if (!info.isLeaf) { + return order; + } + postVisit(info); + return STOP_RECURSION; +} + +bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { + HeightfieldBuffer* buffer = NULL; + if (_depth == _heightDepth) { + int extendedHeightSize = _heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; + int heightContentsSize = extendedHeightSize * extendedHeightSize; + + int extendedColorSize = qMax(_inheritedColorDepth == -1 ? 0 : + signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, + _containedColorDepth == -1 ? 0 : + signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); + int colorContentsSize = extendedColorSize * extendedColorSize; + + int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : + signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, + _containedMaterialDepth == -1 ? 0 : + signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); + int materialContentsSize = extendedMaterialSize * extendedMaterialSize; const HeightfieldBuffer* existingBuffer = static_cast( info.inputValues.at(3).getInlineValue().data()); Box bounds = info.getBounds(); if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize) { - // we already have a buffer of the correct resolution + existingBuffer->getColor().size() == colorContentsSize && + existingBuffer->getMaterial().size() == materialContentsSize) { + // we already have a buffer of the correct resolution addRegion(bounds, existingBuffer->getHeightBounds()); buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), materialContents, materials); - + existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); + } else { // we must create a new buffer and update its borders buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), materialContents, materials); + QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); const Box& heightBounds = buffer->getHeightBounds(); addRegion(bounds, heightBounds); @@ -1919,10 +2009,17 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { _fetchVisitor.init(buffer); _data->guide(_fetchVisitor); } + _heightDepth = _containedColorDepth = _containedMaterialDepth = -1; + } + if (_depth == _inheritedColorDepth) { + _inheritedColorDepth = -1; + } + if (_depth == _inheritedMaterialDepth) { + _inheritedMaterialDepth = -1; } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return STOP_RECURSION; + return true; } void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 08cc1098ca..0dad7ee179 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -249,7 +249,7 @@ public: const QVector& getMaterials() const { return _materials; } QByteArray getUnextendedHeight() const; - QByteArray getUnextendedColor() const; + QByteArray getUnextendedColor(int x = 0, int y = 0) const; int getHeightSize() const { return _heightSize; } float getHeightIncrement() const { return _heightIncrement; } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 3568c15eb2..e5a1f7f7a4 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1038,26 +1038,42 @@ void ImportHeightfieldTool::apply() { data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); - QByteArray color; - if (buffer->getColor().isEmpty()) { - const unsigned char WHITE_VALUE = 0xFF; - color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); - } else { - color = buffer->getUnextendedColor(); - } - HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); - - int size = glm::sqrt(float(height.size())); - 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)) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); + + int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) / + (buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION); + float colorScale = scale / colorUnits; + + for (int y = 0; y < colorUnits; y++) { + for (int x = 0; x < colorUnits; x++) { + MetavoxelData data; + data.setSize(colorScale); + + QByteArray color; + if (buffer->getColor().isEmpty()) { + const unsigned char WHITE_VALUE = 0xFF; + color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); + } else { + color = buffer->getUnextendedColor(x, y); + } + HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode( + AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), + encodeInline(colorPointer)))); + + QByteArray material(height.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 + glm::vec3(x, 0.0f, y) * colorScale, data)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + } + } } } @@ -1143,8 +1159,8 @@ void ImportHeightfieldTool::updatePreview() { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorScale = glm::round(glm::log(_colorImage.height() / _heightImage.height()) / glm::log(2.0f)); - int colorBlockSize = blockSize * pow(2.0, colorScale); + int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f)); + int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0)); int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index a625c71d6a..876864429d 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1198,6 +1198,10 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) return false; } +AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : InlineAttribute(name) { } @@ -1337,6 +1341,10 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post return false; } +AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) : InlineAttribute(name) { } @@ -1404,6 +1412,10 @@ bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool p return maxSize == 0; } +AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index cee01cdbef..5caff17266 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -536,6 +536,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; @@ -580,6 +582,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer HeightfieldMaterialDataPointer; @@ -646,6 +650,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer VoxelColorDataPointer; From f0e4e034b66edd9c844387968100f28ffbbf66d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Oct 2014 18:55:12 -0700 Subject: [PATCH 03/17] Fix for color bytes. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b9baa356f5..5b34a068b5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1970,7 +1970,7 @@ bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, _containedColorDepth == -1 ? 0 : signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int colorContentsSize = extendedColorSize * extendedColorSize; + int colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, From 37569dadd57d938998943a0fb10a92cba0e12599 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Oct 2014 13:00:32 -0700 Subject: [PATCH 04/17] More resolution bits. --- interface/src/MetavoxelSystem.cpp | 97 +++++++++++++++++-- interface/src/MetavoxelSystem.h | 8 ++ .../metavoxels/src/AttributeRegistry.cpp | 61 ++++++++++++ libraries/metavoxels/src/AttributeRegistry.h | 7 ++ .../metavoxels/src/MetavoxelMessages.cpp | 61 ------------ 5 files changed, 164 insertions(+), 70 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 5b34a068b5..49f742450d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -961,6 +961,7 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _scale(scale), _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), _colorBounds(_heightBounds), + _materialBounds(_heightBounds), _height(height), _color(color), _material(material), @@ -968,10 +969,12 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _heightTextureID(0), _colorTextureID(0), _materialTextureID(0), - _heightSize(glm::sqrt(float(height.size()))), + _heightSize(glm::sqrt((float)height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), - _colorSize(glm::sqrt(float(color.size() / DataBlock::COLOR_BYTES))), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { + _colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)), + _colorIncrement(scale / (_colorSize - SHARED_EDGE)), + _materialSize(glm::sqrt((float)material.size())), + _materialIncrement(scale / (_materialSize - SHARED_EDGE)) { _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; @@ -980,6 +983,9 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; + + _materialBounds.maximum.x += _colorIncrement * SHARED_EDGE; + _materialBounds.maximum.z += _colorIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -1742,24 +1748,37 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (height || _heightDepth != -1) { - if (_depth < _heightDepth) { + if (height) { + // to handle borders correctly, make sure we only sample nodes with resolution <= ours + int heightSize = glm::sqrt((float)height->getContents().size()); + float heightIncrement = info.size / heightSize; + if (heightIncrement < _buffer->getHeightIncrement() || _depth < _heightDepth) { height.reset(); } + } + if (height || _heightDepth != -1) { _heightDepth = _depth; } HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color || _colorDepth != -1) { - if (_depth < _colorDepth) { + if (color) { + int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); + float colorIncrement = info.size / colorSize; + if (colorIncrement < _buffer->getColorIncrement() || _depth < _colorDepth) { color.reset(); } + } + if (color || _colorDepth != -1) { _colorDepth = _depth; } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material || _materialDepth != -1) { - if (_depth < _materialDepth) { + if (material) { + int materialSize = glm::sqrt((float)material->getContents().size()); + float materialIncrement = info.size / materialSize; + if (materialIncrement < _buffer->getMaterialIncrement() || _depth < _materialDepth) { material.reset(); } + } + if (material || _materialDepth != -1) { _materialDepth = _depth; } if (!(height || color || material)) { @@ -1861,6 +1880,66 @@ bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { } } if (material) { + const Box& materialBounds = _buffer->getMaterialBounds(); + overlap = materialBounds.getIntersection(overlap); + float materialIncrement = _buffer->getMaterialIncrement(); + int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement; + int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement); + int materialSize = _buffer->getMaterialSize(); + char* dest = _buffer->getMaterial().data() + destY * materialSize + destX; + + const QByteArray& srcMaterial = material->getContents(); + const QVector srcMaterials = material->getMaterials(); + int srcSize = glm::sqrt((float)srcMaterial.size()); + float srcIncrement = info.size / srcSize; + QHash materialMappings; + + if (srcIncrement == materialIncrement) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) { + const uchar* lineSrc = src; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) { + int value = *lineSrc; + if (value != 0) { + int& mapping = materialMappings[value]; + if (mapping == 0) { + mapping = getMaterialIndex(material->getMaterials().at(value - 1), + _buffer->getMaterials(), _buffer->getMaterial()); + } + value = mapping; + } + *lineDest = value; + } + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = materialIncrement / srcIncrement; + for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) { + const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + int value = src[(int)lineSrcX]; + if (value != 0) { + int& mapping = materialMappings[value]; + if (mapping == 0) { + mapping = getMaterialIndex(material->getMaterials().at(value - 1), + _buffer->getMaterials(), _buffer->getMaterial()); + } + value = mapping; + } + *lineDest = value; + } + } + } + clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial()); } } return false; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 0dad7ee179..7e9f2ce2ca 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -236,6 +236,7 @@ public: const Box& getHeightBounds() const { return _heightBounds; } const Box& getColorBounds() const { return _colorBounds; } + const Box& getMaterialBounds() const { return _materialBounds; } QByteArray& getHeight() { return _height; } const QByteArray& getHeight() const { return _height; } @@ -246,6 +247,7 @@ public: QByteArray& getMaterial() { return _material; } const QByteArray& getMaterial() const { return _material; } + QVector& getMaterials() { return _materials; } const QVector& getMaterials() const { return _materials; } QByteArray getUnextendedHeight() const; @@ -257,6 +259,9 @@ public: int getColorSize() const { return _colorSize; } float getColorIncrement() const { return _colorIncrement; } + int getMaterialSize() const { return _materialSize; } + float getMaterialIncrement() const { return _materialIncrement; } + virtual void render(bool cursor = false); private: @@ -265,6 +270,7 @@ private: float _scale; Box _heightBounds; Box _colorBounds; + Box _materialBounds; QByteArray _height; QByteArray _color; QByteArray _material; @@ -277,6 +283,8 @@ private: float _heightIncrement; int _colorSize; float _colorIncrement; + int _materialSize; + float _materialIncrement; typedef QPair BufferPair; static QHash _bufferPairs; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 876864429d..c06e376a25 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1416,6 +1416,67 @@ AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& paren return AttributeValue(parentValue.getAttribute()); } +static QHash countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { + if (!(material && static_cast(material.data())->getDiffuse().isValid())) { + return 0; + } + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& existingMaterial = materials.at(i); + if (existingMaterial) { + if (existingMaterial->equals(material.data())) { + return i + 1; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = material; + return firstEmptyIndex + 1; + } + if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(material); + return materials.size(); + } + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + uchar materialIndex = 0; + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)materialIndex, (char)0); + return materialIndex; +} + +void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } + } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } +} + const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 5caff17266..945c4df94b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -654,6 +654,13 @@ public: virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; +/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, +/// creating a new entry in the list if necessary. +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); + +/// Utility method for editing: removes any unused materials from the supplied list. +void clearUnusedMaterials(QVector& materials, const QByteArray& contents); + typedef QExplicitlySharedDataPointer VoxelColorDataPointer; /// Contains a block of voxel color data. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 549931e030..4c6611f4bd 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -449,67 +449,6 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g _bounds = Box(_position - extents, _position + extents); } -static QHash countIndices(const QByteArray& contents) { - QHash counts; - for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { - if (*src != 0) { - counts[*src]++; - } - } - return counts; -} - -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; From 754d1a28029667ce6292a4c277fabf6a9d5e7f73 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Oct 2014 16:55:55 -0700 Subject: [PATCH 05/17] Another resolution fix. --- interface/src/MetavoxelSystem.cpp | 41 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 49f742450d..d89725e126 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -984,8 +984,8 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; - _materialBounds.maximum.x += _colorIncrement * SHARED_EDGE; - _materialBounds.maximum.z += _colorIncrement * SHARED_EDGE; + _materialBounds.maximum.x += _materialIncrement * SHARED_EDGE; + _materialBounds.maximum.z += _materialIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -1716,11 +1716,11 @@ private: const QVector& _intersections; HeightfieldBuffer* _buffer; - int _heightDepth; - int _colorDepth; - int _materialDepth; + QVector _depthFlags; }; +enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 }; + HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << @@ -1730,15 +1730,17 @@ HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { _buffer = buffer; - _heightDepth = -1; - _colorDepth = -1; - _materialDepth = -1; } int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_buffer->getHeightBounds())) { return STOP_RECURSION; } + if (_depthFlags.size() > _depth) { + _depthFlags[_depth] = 0; + } else { + _depthFlags.append(0); + } if (!info.isLeaf) { return DEFAULT_ORDER; } @@ -1748,38 +1750,39 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + int flags = _depthFlags.at(_depth); if (height) { // to handle borders correctly, make sure we only sample nodes with resolution <= ours int heightSize = glm::sqrt((float)height->getContents().size()); float heightIncrement = info.size / heightSize; - if (heightIncrement < _buffer->getHeightIncrement() || _depth < _heightDepth) { + if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) { height.reset(); + } else { + flags |= HEIGHT_FLAG; } } - if (height || _heightDepth != -1) { - _heightDepth = _depth; - } HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (color) { int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); float colorIncrement = info.size / colorSize; - if (colorIncrement < _buffer->getColorIncrement() || _depth < _colorDepth) { + if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) { color.reset(); + } else { + flags |= COLOR_FLAG; } } - if (color || _colorDepth != -1) { - _colorDepth = _depth; - } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); if (material) { int materialSize = glm::sqrt((float)material->getContents().size()); float materialIncrement = info.size / materialSize; - if (materialIncrement < _buffer->getMaterialIncrement() || _depth < _materialDepth) { + if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) { material.reset(); + } else { + flags |= MATERIAL_FLAG; } } - if (material || _materialDepth != -1) { - _materialDepth = _depth; + if (_depth > 0) { + _depthFlags[_depth - 1] |= flags; } if (!(height || color || material)) { return false; From fd12c7a0d7a03ac63435a6a788bb9eba6e78ff21 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Oct 2014 16:52:19 -0700 Subject: [PATCH 06/17] One more piece of the resolution puzzle. --- interface/src/MetavoxelSystem.cpp | 187 +++++++++------------ libraries/metavoxels/src/MetavoxelData.cpp | 31 ++-- libraries/metavoxels/src/MetavoxelData.h | 2 + 3 files changed, 104 insertions(+), 116 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d89725e126..4c0acc553a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1963,20 +1963,15 @@ private: void addRegion(const Box& unextended, const Box& extended); - int _heightSize; - int _heightDepth; + class DepthInfo { + public: + float minimumColorIncrement; + float minimumMaterialIncrement; + + DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { } + }; - int _inheritedColorSize; - int _inheritedColorDepth; - - int _containedColorSize; - int _containedColorDepth; - - int _inheritedMaterialSize; - int _inheritedMaterialDepth; - - int _containedMaterialSize; - int _containedMaterialDepth; + QVector _depthInfo; QVector _intersections; HeightfieldFetchVisitor _fetchVisitor; @@ -1989,118 +1984,100 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : 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)), - _heightDepth(-1), - _inheritedColorDepth(-1), - _containedColorDepth(-1), - _inheritedMaterialDepth(-1), - _containedMaterialDepth(-1), _fetchVisitor(lod, _intersections) { } -static int signedLeftShift(int value, int shift) { - return (shift > 0) ? value << shift : value >> (-shift); -} - int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - int order = DEFAULT_ORDER; - if (height) { - _heightSize = glm::sqrt((float)height->getContents().size()); - _heightDepth = _depth; - order |= ALL_NODES_REST; - } + DepthInfo depthInfo; HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (color) { - int colorSize = glm::sqrt((float)(color->getContents().size() / DataBlock::COLOR_BYTES)); - if (_heightDepth == -1) { - _inheritedColorSize = colorSize; - _inheritedColorDepth = _depth; - - } else if (_containedColorDepth == -1 || - colorSize > signedLeftShift(_containedColorSize, _containedColorDepth - _depth)) { - _containedColorSize = colorSize; - _containedColorDepth = _depth; - } + int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); + depthInfo.minimumColorIncrement = info.size / colorSize; } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - if (_heightDepth == -1) { - _inheritedMaterialSize = materialSize; - _inheritedMaterialDepth = _depth; - - } else if (_containedMaterialDepth == -1 || - materialSize > signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth)) { - _containedMaterialSize = materialSize; - _containedMaterialDepth = _depth; - } + int materialSize = glm::sqrt((float)material->getContents().size()); + depthInfo.minimumMaterialIncrement = info.size / materialSize; + } + if (_depth < _depthInfo.size()) { + _depthInfo[_depth] = depthInfo; + } else { + _depthInfo.append(depthInfo); } if (!info.isLeaf) { - return order; + return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; } postVisit(info); return STOP_RECURSION; } bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { - HeightfieldBuffer* buffer = NULL; - if (_depth == _heightDepth) { - int extendedHeightSize = _heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedHeightSize * extendedHeightSize; - - int extendedColorSize = qMax(_inheritedColorDepth == -1 ? 0 : - signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, - _containedColorDepth == -1 ? 0 : - signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; - - int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : - signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, - _containedMaterialDepth == -1 ? 0 : - signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int materialContentsSize = extendedMaterialSize * extendedMaterialSize; - - const HeightfieldBuffer* existingBuffer = static_cast( - info.inputValues.at(3).getInlineValue().data()); - Box bounds = info.getBounds(); - if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize && - existingBuffer->getMaterial().size() == materialContentsSize) { - // we already have a buffer of the correct resolution - addRegion(bounds, existingBuffer->getHeightBounds()); - buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - - } else { - // we must create a new buffer and update its borders - buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); - const Box& heightBounds = buffer->getHeightBounds(); - addRegion(bounds, heightBounds); + const DepthInfo& depthInfo = _depthInfo.at(_depth); + if (_depth > 0) { + DepthInfo& parentDepthInfo = _depthInfo[_depth - 1]; + parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement); + parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement, + depthInfo.minimumMaterialIncrement); + } + if (_visitations.at(_depth).isInputLeaf(0)) { + HeightfieldBuffer* buffer = NULL; + HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + if (height) { + int heightSize = glm::sqrt((float)height->getContents().size()); + int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; + int heightContentsSize = extendedHeightSize * extendedHeightSize; + float minimumColorIncrement = depthInfo.minimumColorIncrement; + float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement; + for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) { + const DepthInfo& ancestorDepthInfo = _depthInfo.at(i); + minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement); + minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement); + } + int colorContentsSize = 0; + if (minimumColorIncrement != FLT_MAX) { + int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE; + colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES; + } + int materialContentsSize = 0; + if (minimumMaterialIncrement != FLT_MAX) { + int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE; + materialContentsSize = materialSize * materialSize; + } + const HeightfieldBuffer* existingBuffer = static_cast( + info.inputValues.at(3).getInlineValue().data()); + Box bounds = info.getBounds(); + if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && + existingBuffer->getColor().size() == colorContentsSize && + existingBuffer->getMaterial().size() == materialContentsSize) { + // we already have a buffer of the correct resolution + addRegion(bounds, existingBuffer->getHeightBounds()); + buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), + existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - _intersections.clear(); - _intersections.append(Box(heightBounds.minimum, - glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); - _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), - glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); - _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), - heightBounds.maximum)); - _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), - glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); - - _fetchVisitor.init(buffer); - _data->guide(_fetchVisitor); + } else { + // we must create a new buffer and update its borders + buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), + QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); + const Box& heightBounds = buffer->getHeightBounds(); + addRegion(bounds, heightBounds); + + _intersections.clear(); + _intersections.append(Box(heightBounds.minimum, + glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); + _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), + glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); + _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), + heightBounds.maximum)); + _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), + glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); + + _fetchVisitor.init(buffer); + _data->guide(_fetchVisitor); + } } - _heightDepth = _containedColorDepth = _containedMaterialDepth = -1; + BufferDataPointer pointer(buffer); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); } - if (_depth == _inheritedColorDepth) { - _inheritedColorDepth = -1; - } - if (_depth == _inheritedMaterialDepth) { - _inheritedMaterialDepth = -1; - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return true; } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2f43f8f4ae..b79903ba8a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1509,7 +1509,7 @@ bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } -static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float lodBase, int encodedOrder) { +static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int encodedOrder) { MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); nextVisitation.info.size = visitation.info.size * 0.5f; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { @@ -1519,14 +1519,14 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); const AttributeValue& parentValue = visitation.info.inputValues.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } @@ -1604,9 +1604,10 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * + visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); + visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * + visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor->visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { @@ -1628,14 +1629,15 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { return true; } - return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, lodBase, encodedOrder)); + return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, encodedOrder)); } bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * + visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); + visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * + visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor->visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { @@ -1658,7 +1660,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { return true; } if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) { - return defaultGuideToChildren(visitation, lodBase, encodedOrder); + return defaultGuideToChildren(visitation, encodedOrder); } bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES); MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); @@ -1672,7 +1674,8 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); const AttributeValue& parentValue = visitation.info.inputValues.at(j); - bool expand = (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier()); + bool expand = (visitation.info.size >= visitation.info.lodBase * + parentValue.getAttribute()->getLODThresholdMultiplier()); MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); @@ -1686,7 +1689,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } @@ -1911,6 +1914,12 @@ MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitation::MetavoxelVisitation() { } +bool MetavoxelVisitation::isInputLeaf(int index) const { + MetavoxelNode* node = inputNodes.at(index); + return !node || node->isLeaf() || info.size < info.lodBase * + info.inputValues.at(index).getAttribute()->getLODThresholdMultiplier(); +} + bool MetavoxelVisitation::allInputNodesLeaves() const { foreach (MetavoxelNode* node, inputNodes) { if (node && !node->isLeaf()) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 8a240b9d7b..3efb0fc8f2 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -278,6 +278,7 @@ public: float size; ///< the size of the voxel in all dimensions QVector inputValues; QVector outputValues; + float lodBase; bool isLODLeaf; bool isLeaf; @@ -537,6 +538,7 @@ public: MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize); MetavoxelVisitation(); + bool isInputLeaf(int index) const; bool allInputNodesLeaves() const; AttributeValue getInheritedOutputValue(int index) const; }; From 2314ab36243321da82ca11c97e2ad8de31a82323 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Oct 2014 18:05:40 -0700 Subject: [PATCH 07/17] Removed unused variable, fixed painting materials on heightfields with greater resolution. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 4c6611f4bd..c9c2d32b7e 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -445,7 +445,8 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g _material(material), _color(color) { - glm::vec3 extents(_radius, _radius, _radius); + const float LARGE_EXTENT = 100000.0f; + glm::vec3 extents(_radius, LARGE_EXTENT, _radius); _bounds = Box(_position - extents, _position + extents); } @@ -520,7 +521,6 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { 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++) { From ccc5e0d6cafeb0dd5bb68a1ae94baade27725b37 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 31 Oct 2014 10:06:43 -0700 Subject: [PATCH 08/17] Increase Interface's disk cache from default 50MB to 10GB --- interface/src/Application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0752f9fa9e..2a8b15ad39 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -109,6 +109,9 @@ static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged c const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) + +const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB + static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml"; @@ -347,9 +350,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); From 2746733687c78d150d3e498f379007ab8c58685c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 10:46:17 -0800 Subject: [PATCH 09/17] don't allow HifiSockAddr lookup to block application --- libraries/networking/src/HifiSockAddr.cpp | 33 +++++++++++++++----- libraries/networking/src/HifiSockAddr.h | 8 +++-- libraries/networking/src/LimitedNodeList.cpp | 6 ++-- libraries/networking/src/LimitedNodeList.h | 1 + 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 97e9721356..2cb0aaad83 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -36,14 +36,15 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { - // lookup the IP by the hostname - QHostInfo hostInfo = QHostInfo::fromName(hostname); - foreach(const QHostAddress& address, hostInfo.addresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - _address = address; - _port = hostOrderPort; - } +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : + _address(hostname), + _port(hostOrderPort) +{ + // if we parsed an IPv4 address out of the hostname, don't look it up + if (_address.protocol() != QAbstractSocket::IPv4Protocol) { + // sync lookup the IP by the hostname + int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); + qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; } } @@ -75,6 +76,22 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { return _address == rhsSockAddr._address && _port == rhsSockAddr._port; } +void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { + if (hostInfo.error() != QHostInfo::NoError) { + qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); + } + + foreach(const QHostAddress& address, hostInfo.addresses()) { + // just take the first IPv4 address + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + qDebug() << "QHostInfo lookup result for" + << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); + break; + } + } +} + QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; return debug.space(); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 42f815390a..50dc99f4df 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,9 +19,11 @@ #include #endif -#include +#include +#include -class HifiSockAddr { +class HifiSockAddr : public QObject { + Q_OBJECT public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); @@ -51,6 +53,8 @@ public: friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); +private slots: + void handleLookupResult(const QHostInfo& hostInfo); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5c4dc6cea2..043f0621bb 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -73,6 +73,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _dtlsSocket(NULL), _localSockAddr(), _publicSockAddr(), + _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT), _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer() @@ -583,11 +584,8 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); - // lookup the IP for the STUN server - HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); - _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), - stunSockAddr.getAddress(), stunSockAddr.getPort()); + _stunSockAddr.getAddress(), _stunSockAddr.getPort()); } void LimitedNodeList::rebindNodeSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 73381d01a5..64495fbd34 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -163,6 +163,7 @@ protected: QUdpSocket* _dtlsSocket; HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; + HifiSockAddr _stunSockAddr; int _numCollectedPackets; int _numCollectedBytes; QElapsedTimer _packetStatTimer; From d085be6c4709c8c4ea7b0ca1217c2c11888261f0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Nov 2014 11:14:04 -0800 Subject: [PATCH 10/17] Better estimate of HMD offset --- examples/leapHands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/leapHands.js b/examples/leapHands.js index c9c3af0063..437637dc3f 100644 --- a/examples/leapHands.js +++ b/examples/leapHands.js @@ -16,7 +16,7 @@ var leapHands = (function () { var isOnHMD, LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD", LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip - HMD_OFFSET = 0.100, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief + HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief hands, wrists, NUM_HANDS = 2, // 0 = left; 1 = right From 92a6609ac00dee85a7eb8b8ef89ab61e4450029a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 3 Nov 2014 11:31:04 -0800 Subject: [PATCH 11/17] simpler tuning system for walk velocities --- interface/src/avatar/MyAvatar.cpp | 53 ++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 623631d5e5..b468f37f33 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1269,6 +1269,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe _isPushing = false; float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); + glm::vec3 newLocalVelocity = localVelocity; float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); @@ -1285,31 +1286,47 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe if (directionLength > EPSILON) { direction /= directionLength; - // Compute the target keyboard velocity (which ramps up slowly, and damps very quickly) - // the max magnitude of which depends on what we're doing: - float motorSpeed = glm::length(_keyboardMotorVelocity); - float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED; - float speedGrowthTimescale = 2.0f; - float speedIncreaseFactor = 1.8f; - motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; - const float maxBoostSpeed = _scale * MAX_BOOST_SPEED; - if (motorSpeed < maxBoostSpeed) { - // an active keyboard motor should never be slower than this - float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; - motorSpeed += MIN_AVATAR_SPEED * boostCoefficient; - motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient; - } else if (motorSpeed > finalMaxMotorSpeed) { - motorSpeed = finalMaxMotorSpeed; + if (hasFloor) { + // we're walking --> simple exponential decay toward target walk speed + const float WALK_ACCELERATION_TIMESCALE = 0.7f; // seconds to decrease delta to 1/e + _keyboardMotorVelocity = MAX_WALKING_SPEED * direction; + motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f); + + } else { + // we're flying --> more complex curve + float motorSpeed = glm::length(_keyboardMotorVelocity); + float finalMaxMotorSpeed = _scale * MAX_KEYBOARD_MOTOR_SPEED; + float speedGrowthTimescale = 2.0f; + float speedIncreaseFactor = 1.8f; + motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; + const float maxBoostSpeed = _scale * MAX_BOOST_SPEED; + if (motorSpeed < maxBoostSpeed) { + // an active keyboard motor should never be slower than this + float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; + motorSpeed += MIN_AVATAR_SPEED * boostCoefficient; + motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient; + } else if (motorSpeed > finalMaxMotorSpeed) { + motorSpeed = finalMaxMotorSpeed; + } + _keyboardMotorVelocity = motorSpeed * direction; } - _keyboardMotorVelocity = motorSpeed * direction; _isPushing = true; } + newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); } else { _keyboardMotorVelocity = glm::vec3(0.0f); + newLocalVelocity = (1.0f - motorEfficiency) * localVelocity; + if (hasFloor && !_wasPushing) { + float speed = glm::length(newLocalVelocity); + if (speed > MIN_AVATAR_SPEED) { + // add small constant friction to help avatar drift to a stop sooner at low speeds + const float CONSTANT_FRICTION_DECELERATION = MIN_AVATAR_SPEED / 0.20f; + newLocalVelocity *= (speed - timescale * CONSTANT_FRICTION_DECELERATION) / speed; + } + } } - // apply keyboard motor - return localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); + return newLocalVelocity; } glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVelocity) { From e976942515f6666699bf80ae9933f7ae0fbb2e4a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 11:44:15 -0800 Subject: [PATCH 12/17] fix includes for HifiSockAddr --- libraries/networking/src/HifiSockAddr.cpp | 6 +++--- libraries/networking/src/HifiSockAddr.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 2cb0aaad83..3a200fd392 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include +#include +#include #include "HifiSockAddr.h" diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 50dc99f4df..0aa6779589 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,7 +19,6 @@ #include #endif -#include #include class HifiSockAddr : public QObject { From 9e1da6489534ea85409840c1d60c7f977355b740 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:03:10 -0800 Subject: [PATCH 13/17] use different include style for QHostInfo --- libraries/networking/src/HifiSockAddr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 0aa6779589..5bbd27437b 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,7 +19,7 @@ #include #endif -#include +#include class HifiSockAddr : public QObject { Q_OBJECT From e53ad09b3f8218b4465591e6f64c237f5872b123 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:24:17 -0800 Subject: [PATCH 14/17] rename the automatic networking update method to heartbeat --- domain-server/src/DomainServer.cpp | 16 ++++++++-------- domain-server/src/DomainServer.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f99c1d280e..22c194ffb0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -361,11 +361,11 @@ void DomainServer::setupAutomaticNetworking() { iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes - connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer); - connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer); + connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); + connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); // tell the data server which type of automatic networking we are using - updateNetworkingInfoWithDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(automaticNetworkValue); } // attempt to update our sockets now @@ -378,7 +378,7 @@ void DomainServer::setupAutomaticNetworking() { return; } } else { - updateNetworkingInfoWithDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(automaticNetworkValue); } } @@ -1081,10 +1081,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - updateNetworkingInfoWithDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); + sendHeartbeatToDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); } -void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress) { +void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID(); @@ -1112,11 +1112,11 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, // todo: have data-web respond with ice-server hostname to use void DomainServer::performICEUpdates() { - sendHearbeatToIceServer(); + sendHeartbeatToIceServer(); sendICEPingPackets(); } -void DomainServer::sendHearbeatToIceServer() { +void DomainServer::sendHeartbeatToIceServer() { const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT); LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR); } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5e4da00601..13bc4a0474 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -66,7 +66,8 @@ private slots: void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); - void sendHearbeatToIceServer(); + void sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress = QString()); + void sendHeartbeatToIceServer(); void sendICEPingPackets(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); @@ -76,7 +77,6 @@ private: bool optionallySetupAssignmentPayment(); void setupAutomaticNetworking(); - void updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress = QString()); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEHeartbeatResponse(const QByteArray& packet); From d50a8f630982f8555d9f6cc9a2fe612f04902e95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:40:56 -0800 Subject: [PATCH 15/17] send a heartbeat to data server every 15s --- domain-server/src/DomainServer.cpp | 34 ++++++++++++++++++------------ domain-server/src/DomainServer.h | 5 ++++- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 22c194ffb0..3cea933246 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -50,6 +50,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _hostname(), _webAuthenticationStateSet(), _cookieSessionHash(), + _automaticNetworkingSetting(), _settingsManager() { LogUtils::init(); @@ -327,17 +328,17 @@ void DomainServer::setupAutomaticNetworking() { return; } - QString automaticNetworkValue = + _automaticNetworkingSetting = _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); - if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || - automaticNetworkValue == FULL_AUTOMATIC_NETWORKING_VALUE) { + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || + _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); const QUuid& domainID = nodeList->getSessionUUID(); if (!domainID.isNull()) { - qDebug() << "domain-server" << automaticNetworkValue << "automatic networking enabled for ID" + qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID" << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; @@ -347,7 +348,7 @@ void DomainServer::setupAutomaticNetworking() { QTimer* dynamicIPTimer = new QTimer(this); connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); - if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS); // send public socket changes to the data server so nodes can find us at our new IP @@ -364,8 +365,8 @@ void DomainServer::setupAutomaticNetworking() { connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); - // tell the data server which type of automatic networking we are using - sendHeartbeatToDataServer(automaticNetworkValue); + // send our heartbeat to data server so it knows what our network settings are + sendHeartbeatToDataServer(); } // attempt to update our sockets now @@ -378,8 +379,17 @@ void DomainServer::setupAutomaticNetworking() { return; } } else { - sendHeartbeatToDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(); } + + qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; + + // no matter the auto networking settings we should heartbeat to the data-server every 15s + const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS = 15; + + QTimer* dataHeartbeatTimer = new QTimer(this); + connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer); + dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS); } void DomainServer::loginFailed() { @@ -1081,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - sendHeartbeatToDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); + updateDomainInDataServer(newPublicSockAddr.getAddress().toString()); } -void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress) { +void DomainServer::updateDomainInDataServer(const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID(); @@ -1097,9 +1107,7 @@ void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QS domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress; } - qDebug() << "Updating automatic networking setting in domain-server to" << newSetting; - - domainObject[AUTOMATIC_NETWORKING_KEY] = newSetting; + domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting; QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 13bc4a0474..e5f321639c 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -66,7 +66,7 @@ private slots: void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); - void sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress = QString()); + void sendHeartbeatToDataServer() { updateDomainInDataServer(); } void sendHeartbeatToIceServer(); void sendICEPingPackets(); private: @@ -77,6 +77,7 @@ private: bool optionallySetupAssignmentPayment(); void setupAutomaticNetworking(); + void updateDomainInDataServer(const QString& networkAddress = QString()); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEHeartbeatResponse(const QByteArray& packet); @@ -150,6 +151,8 @@ private: QHash _connectingICEPeers; QHash _connectedICEPeers; + QString _automaticNetworkingSetting; + DomainServerSettingsManager _settingsManager; }; From eb856095a962033a7984c69f7285d5a435b947ff Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:49:24 -0800 Subject: [PATCH 16/17] fix the domain server heartbeat timer --- domain-server/src/DomainServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3cea933246..c6ab0285ea 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -385,11 +385,11 @@ void DomainServer::setupAutomaticNetworking() { qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; // no matter the auto networking settings we should heartbeat to the data-server every 15s - const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS = 15; + const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000; QTimer* dataHeartbeatTimer = new QTimer(this); connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer); - dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS); + dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS); } void DomainServer::loginFailed() { From fabc3aea5709960744a72f187c86573bb5351302 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:54:28 -0800 Subject: [PATCH 17/17] Revert "don't allow a HifiSockAddr lookup to block application" --- libraries/networking/src/HifiSockAddr.cpp | 39 ++++++-------------- libraries/networking/src/HifiSockAddr.h | 7 +--- libraries/networking/src/LimitedNodeList.cpp | 6 ++- libraries/networking/src/LimitedNodeList.h | 1 - 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 3a200fd392..97e9721356 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include +#include +#include #include "HifiSockAddr.h" @@ -36,15 +36,14 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : - _address(hostname), - _port(hostOrderPort) -{ - // if we parsed an IPv4 address out of the hostname, don't look it up - if (_address.protocol() != QAbstractSocket::IPv4Protocol) { - // sync lookup the IP by the hostname - int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); - qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { + // lookup the IP by the hostname + QHostInfo hostInfo = QHostInfo::fromName(hostname); + foreach(const QHostAddress& address, hostInfo.addresses()) { + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + _port = hostOrderPort; + } } } @@ -76,22 +75,6 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { return _address == rhsSockAddr._address && _port == rhsSockAddr._port; } -void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { - if (hostInfo.error() != QHostInfo::NoError) { - qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); - } - - foreach(const QHostAddress& address, hostInfo.addresses()) { - // just take the first IPv4 address - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - _address = address; - qDebug() << "QHostInfo lookup result for" - << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); - break; - } - } -} - QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; return debug.space(); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 5bbd27437b..42f815390a 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,10 +19,9 @@ #include #endif -#include +#include -class HifiSockAddr : public QObject { - Q_OBJECT +class HifiSockAddr { public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); @@ -52,8 +51,6 @@ public: friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); -private slots: - void handleLookupResult(const QHostInfo& hostInfo); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 043f0621bb..5c4dc6cea2 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -73,7 +73,6 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _dtlsSocket(NULL), _localSockAddr(), _publicSockAddr(), - _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT), _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer() @@ -584,8 +583,11 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); + // lookup the IP for the STUN server + HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); + _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), - _stunSockAddr.getAddress(), _stunSockAddr.getPort()); + stunSockAddr.getAddress(), stunSockAddr.getPort()); } void LimitedNodeList::rebindNodeSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 64495fbd34..73381d01a5 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -163,7 +163,6 @@ protected: QUdpSocket* _dtlsSocket; HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; - HifiSockAddr _stunSockAddr; int _numCollectedPackets; int _numCollectedBytes; QElapsedTimer _packetStatTimer;