From 1e6aee1872af18ba697421f59c3427de36a2af24 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 17 Oct 2014 15:23:54 -0700 Subject: [PATCH] Basic heightfield conversion. --- libraries/metavoxels/src/MetavoxelData.cpp | 68 +++++++ libraries/metavoxels/src/MetavoxelData.h | 22 ++- .../metavoxels/src/MetavoxelMessages.cpp | 168 +++++++++++++----- 3 files changed, 216 insertions(+), 42 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index e9cb797114..43c2969192 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -2045,6 +2045,27 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire return _bounds.findRayIntersection(origin, direction, distance); } +bool Spanner::hasOwnColors() const { + return false; +} + +bool Spanner::hasOwnMaterials() const { + return false; +} + +QRgb Spanner::getColor(const glm::vec3& point) { + return 0; +} + +int Spanner::getMaterial(const glm::vec3& point) { + return 0; +} + +QVector& Spanner::getMaterials() { + static QVector emptyMaterials; + return emptyMaterials; +} + bool Spanner::contains(const glm::vec3& point) { return false; } @@ -2381,6 +2402,53 @@ Heightfield::Heightfield(const Box& bounds, float increment, const QByteArray& h setBounds(bounds); } +bool Heightfield::hasOwnColors() const { + return true; +} + +bool Heightfield::hasOwnMaterials() const { + return true; +} + +QRgb Heightfield::getColor(const glm::vec3& point) { + glm::vec3 relative = (point - getBounds().minimum) / _increment; + glm::vec3 floors = glm::floor(relative); + glm::vec3 ceils = glm::ceil(relative); + glm::vec3 fracts = glm::fract(relative); + int floorX = (int)floors.x; + int floorZ = (int)floors.z; + int ceilX = (int)ceils.x; + int ceilZ = (int)ceils.z; + const uchar* src = (const uchar*)_color.constData(); + const uchar* upperLeft = src + (floorZ * _width + floorX) * DataBlock::COLOR_BYTES; + const uchar* lowerRight = src + (ceilZ * _width + ceilX) * DataBlock::COLOR_BYTES; + glm::vec3 interpolatedColor = glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z); + + // the final vertex (and thus which triangle we check) depends on which half we're on + if (fracts.x >= fracts.z) { + const uchar* upperRight = src + (floorZ * _width + ceilX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(interpolatedColor, glm::mix(glm::vec3(upperRight[0], upperRight[1], upperRight[2]), + glm::vec3(lowerRight[0], lowerRight[1], lowerRight[2]), fracts.z), (fracts.x - fracts.z) / (1.0f - fracts.z)); + + } else { + const uchar* lowerLeft = src + (ceilZ * _width + floorX) * DataBlock::COLOR_BYTES; + interpolatedColor = glm::mix(glm::mix(glm::vec3(upperLeft[0], upperLeft[1], upperLeft[2]), + glm::vec3(lowerLeft[0], lowerLeft[1], lowerLeft[2]), fracts.z), interpolatedColor, fracts.x / fracts.z); + } + return qRgb(interpolatedColor.r, interpolatedColor.g, interpolatedColor.b); +} + +int Heightfield::getMaterial(const glm::vec3& point) { + glm::vec3 relative = (point - getBounds().minimum) / _increment; + const uchar* src = (const uchar*)_material.constData(); + return src[(int)glm::round(relative.z) * _width + (int)glm::round(relative.x)]; +} + +QVector& Heightfield::getMaterials() { + return _materials; +} + bool Heightfield::contains(const glm::vec3& point) { if (!getBounds().contains(point)) { return false; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 42aa3e321b..4866331e26 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -644,9 +644,24 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& clipMinimum, float clipSize, float& distance) const; + /// Checks whether this spanner has its own colors. + virtual bool hasOwnColors() const; + + /// Checks whether this spanner has its own materials. + virtual bool hasOwnMaterials() const; + /// Checks whether the spanner contains the specified point. virtual bool contains(const glm::vec3& point); + /// Retrieves the color at the specified point. + virtual QRgb getColor(const glm::vec3& point); + + /// Retrieves the material at the specified point. + virtual int getMaterial(const glm::vec3& point); + + /// Retrieves a reference to the list of materials. + virtual QVector& getMaterials(); + /// Finds the intersection, if any, between the specified line segment and the spanner. virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); @@ -860,7 +875,12 @@ public: QByteArray& getHeight() { return _height; } QByteArray& getColor() { return _color; } QByteArray& getMaterial() { return _material; } - QVector& getMaterials() { return _materials; } + + virtual bool hasOwnColors() const; + virtual bool hasOwnMaterials() const; + virtual QRgb getColor(const glm::vec3& point); + virtual int getMaterial(const glm::vec3& point); + virtual QVector& getMaterials(); virtual bool contains(const glm::vec3& point); virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 5898d7cf6f..0106636732 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -705,15 +705,31 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { bool flipped = (qAlpha(rgb) == 0); float step = 1.0f / scale; glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step); - for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - *destX = rgb; + if (_spanner->hasOwnColors()) { + for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { + position.y = info.minimum.y + minY * step; + for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y += step) { + position.x = info.minimum.x + minX * step; + for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { + if (_spanner->contains(position)) { + *destX = _spanner->getColor(position); + } + } + } + } + } else { + for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { + position.y = info.minimum.y + minY * step; + for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y += step) { + position.x = info.minimum.x + minX * step; + for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { + if (_spanner->contains(position)) { + *destX = rgb; + } } } } @@ -829,18 +845,45 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(), encodeInline(newHermitePointer)); - uchar materialIndex = getMaterialIndex(_material, materials, materialContents); - position.z = info.minimum.z + minZ * step; - for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, - *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { - position.y = info.minimum.y + minY * step; - for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; - destY += VOXEL_BLOCK_SAMPLES, position.y += step) { - position.x = info.minimum.x + minX * step; - for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { - if (_spanner->contains(position)) { - *destX = materialIndex; - } + if (_spanner->hasOwnMaterials()) { + QHash materialMap; + position.z = info.minimum.z + minZ * step; + for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { + position.y = info.minimum.y + minY * step; + for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y += step) { + position.x = info.minimum.x + minX * step; + for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { + if (_spanner->contains(position)) { + int material = _spanner->getMaterial(position); + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + mapping = getMaterialIndex(_spanner->getMaterials().at(material - 1), materials, + materialContents); + } + material = mapping; + } + *destX = material; + } + } + } + } + } else { + uchar materialIndex = getMaterialIndex(_material, materials, materialContents); + position.z = info.minimum.z + minZ * step; + for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, + *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) { + position.y = info.minimum.y + minY * step; + for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; + destY += VOXEL_BLOCK_SAMPLES, position.y += step) { + position.x = info.minimum.x + minX * step; + for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) { + if (_spanner->contains(position)) { + *destX = materialIndex; + } + } } } } @@ -935,7 +978,7 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { float increment = 1.0f / heightScale; if (!spanner) { _spannerBounds.minimum = glm::floor(_bounds.minimum / increment) * increment; - _spannerBounds.maximum = glm::ceil(_bounds.maximum / increment) * increment; + _spannerBounds.maximum = (glm::ceil(_bounds.maximum / increment) + glm::vec3(1.0f, 0.0f, 1.0f)) * increment; _spannerBounds.minimum.y = bounds.minimum.y; _spannerBounds.maximum.y = bounds.maximum.y; _heightfieldWidth = (int)glm::round((_spannerBounds.maximum.x - _spannerBounds.minimum.x) / increment) + 1; @@ -950,8 +993,8 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { overlap = bounds.getIntersection(_spannerBounds); int destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; int destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; - int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale) + 1; - int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale) + 1; + int destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); + int destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); char* dest = spanner->getHeight().data() + destY * _heightfieldWidth + destX; srcX = (overlap.minimum.x - info.minimum.x) * heightScale; srcY = (overlap.minimum.z - info.minimum.z) * heightScale; @@ -962,14 +1005,16 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { } // clear the inner area - Box innerBounds = _bounds; + Box innerBounds = _spannerBounds; innerBounds.minimum.x += increment; innerBounds.minimum.z += increment; - overlap = bounds.getIntersection(innerBounds); - destX = (overlap.minimum.x - info.minimum.x) * heightScale; - destY = (overlap.minimum.z - info.minimum.z) * heightScale; - destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); + innerBounds.maximum.x -= increment; + innerBounds.maximum.z -= increment; + Box innerOverlap = bounds.getIntersection(innerBounds); + destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; + destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; + destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); + destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); dest = contents.data() + destY * size + destX; for (int y = 0; y < destHeight; y++, dest += size) { @@ -1006,11 +1051,26 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES); heightScale = size / info.size; - int destX = (overlap.minimum.x - info.minimum.x) * heightScale; - int destY = (overlap.minimum.z - info.minimum.z) * heightScale; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES; + // copy the inner area + destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; + destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; + destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); + destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); + dest = spanner->getColor().data() + (destY * _heightfieldWidth + destX) * DataBlock::COLOR_BYTES; + srcX = (overlap.minimum.x - info.minimum.x) * heightScale; + srcY = (overlap.minimum.z - info.minimum.z) * heightScale; + src = contents.data() + (srcY * size + srcX) * DataBlock::COLOR_BYTES; + + for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth * DataBlock::COLOR_BYTES, + src += size * DataBlock::COLOR_BYTES) { + memcpy(dest, src, destWidth * DataBlock::COLOR_BYTES); + } + + destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; + destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; + destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); + destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); + dest = contents.data() + (destY * size + destX) * DataBlock::COLOR_BYTES; for (int y = 0; y < destHeight; y++, dest += size * DataBlock::COLOR_BYTES) { memset(dest, 0, destWidth * DataBlock::COLOR_BYTES); @@ -1023,20 +1083,46 @@ int HeightfieldClearFetchVisitor::visit(MetavoxelInfo& info) { HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue(); if (materialPointer) { contents = materialPointer->getContents(); + QVector materials = materialPointer->getMaterials(); size = glm::sqrt((float)contents.size()); heightScale = size / info.size; - int destX = (overlap.minimum.x - info.minimum.x) * heightScale; - int destY = (overlap.minimum.z - info.minimum.z) * heightScale; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) * heightScale); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) * heightScale); - char* dest = contents.data() + destY * size + destX; + // copy the inner area + destX = (overlap.minimum.x - _spannerBounds.minimum.x) * heightScale; + destY = (overlap.minimum.z - _spannerBounds.minimum.z) * heightScale; + destWidth = (int)glm::round((overlap.maximum.x - overlap.minimum.x) * heightScale); + destHeight = (int)glm::round((overlap.maximum.z - overlap.minimum.z) * heightScale); + uchar* dest = (uchar*)spanner->getMaterial().data() + destY * _heightfieldWidth + destX; + srcX = (overlap.minimum.x - info.minimum.x) * heightScale; + srcY = (overlap.minimum.z - info.minimum.z) * heightScale; + uchar* src = (uchar*)contents.data() + srcY * size + srcX; + QHash materialMap; + + for (int y = 0; y < destHeight; y++, dest += _heightfieldWidth, src += size) { + for (uchar* lineSrc = src, *lineDest = dest, *end = src + destWidth; lineSrc != end; lineSrc++, lineDest++) { + int material = *lineSrc; + if (material != 0) { + int& mapping = materialMap[material]; + if (mapping == 0) { + mapping = getMaterialIndex(materials.at(material - 1), spanner->getMaterials(), + spanner->getMaterial()); + } + material = mapping; + } + *lineDest = material; + } + } + + destX = (innerOverlap.minimum.x - info.minimum.x) * heightScale; + destY = (innerOverlap.minimum.z - info.minimum.z) * heightScale; + destWidth = glm::ceil((innerOverlap.maximum.x - innerOverlap.minimum.x) * heightScale); + destHeight = glm::ceil((innerOverlap.maximum.z - innerOverlap.minimum.z) * heightScale); + dest = (uchar*)contents.data() + destY * size + destX; for (int y = 0; y < destHeight; y++, dest += size) { memset(dest, 0, destWidth); } - QVector materials = materialPointer->getMaterials(); clearUnusedMaterials(materials, contents); HeightfieldMaterialDataPointer newMaterialPointer(new HeightfieldMaterialData(contents, materials)); info.outputValues[2] = AttributeValue(_outputs.at(2),