} if (_depth < _depthInfo.size()) { _depthInfo[_depth] = depthInfo; } else { _depthInfo.append(depthInfo); } if (!info.isLeaf) { return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; } postVisit(info); return STOP_RECURSION;} bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { 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<HeightfieldHeightDataPointer>(); 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<const HeightfieldBuffer*>( info.inputValues.at(3).getInlineValue<BufferDataPointer>().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); _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); } } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); } return true;} void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { regions.append(unextended); regionBounds.add(extended);} class HeightfieldUpdateVisitor : public MetavoxelVisitor {public: HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds); virtual int visit(MetavoxelInfo& info); private: const QVector<Box>& _regions; const Box& _regionBounds; QVector<Box> _intersections; HeightfieldFetchVisitor _fetchVisitor;}; HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), _regions(regions), _regionBounds(regionBounds), _fetchVisitor(lod, _intersections) {} int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_regionBounds)) { return STOP_RECURSION; } if (!info.isLeaf) { return DEFAULT_ORDER; } const HeightfieldBuffer* buffer = static_cast<const HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } _intersections.clear(); foreach (const Box& region, _regions) { if (region.intersects(buffer->getHeightBounds())) { _intersections.append(region.getIntersection(buffer->getHeightBounds())); } } if (_intersections.isEmpty()) { return STOP_RECURSION; } HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials()); _fetchVisitor.init(newBuffer); _data->guide(_fetchVisitor); BufferDataPointer pointer(newBuffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return STOP_RECURSION;} class VoxelAugmentVisitor : public MetavoxelVisitor {public: VoxelAugmentVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info);}; VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << AttributeRegistry::getInstance()->getVoxelHermiteAttribute(), QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) {} class EdgeCrossing {public: glm::vec3 point; glm::vec3 normal; QRgb color; char material; int axis;}; class AxisIndex {public: int x, y, z; AxisIndex(int x = -1, int y = -1, int z = -1) : x(x), y(y), z(z) { }}; static glm::vec3 safeNormalize(const glm::vec3& vector) { float length = glm::length(vector); return (length > 0.0f) ? (vector / length) : vector;} int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } BufferData* buffer = NULL; VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>(); VoxelMaterialDataPointer material = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>(); VoxelHermiteDataPointer hermite = info.inputValues.at(2).getInlineValue<VoxelHermiteDataPointer>(); if (color && hermite) { QVector<VoxelPoint> vertices; QVector<int> indices; QVector<glm::vec3> hermiteSegments; QMultiHash<QRgb, int> quadIndices; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges const QVector<QRgb>& colorContents = color->getContents(); const QVector<QRgb>& hermiteContents = hermite->getContents(); int size = color->getSize(); int area = size * size; // number variables such as offset3 and alpha0 in this function correspond to cube corners, where the x, y, and z // components are represented as bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at // the minimum x, y, and z corner and alpha7 is the value at the maximum x, y, and z int offset3 = size + 1; int offset5 = area + 1; int offset6 = area + size; int offset7 = area + size + 1; const QRgb* colorZ = colorContents.constData(); const QRgb* hermiteData = hermiteContents.constData(); int hermiteStride = hermite->getSize() * VoxelHermiteData::EDGE_COUNT; int hermiteArea = hermiteStride * hermite->getSize(); const char* materialData = material ? material->getContents().constData() : NULL; // as we scan down the cube generating vertices between grid points, we remember the indices of the last // (element, line, section--x, y, z) so that we can connect generated vertices as quads int expanded = size + 1; QVector<AxisIndex> lineIndices(expanded); QVector<AxisIndex> lastLineIndices(expanded); QVector<AxisIndex> planeIndices(expanded * expanded); QVector<AxisIndex> lastPlaneIndices(expanded * expanded); const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; float highest = size - 1.0f; float scale = info.size / highest; const int ALPHA_OFFSET = 24; bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); for (int z = 0; z < expanded; z++) { const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { AxisIndex lastIndex; const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { int alpha0 = colorX[0] >> ALPHA_OFFSET; int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; int alphaTotal = alpha0; int possibleTotal = EIGHT_BIT_MAXIMUM; // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between // neighboring blocks, which share only one layer of points bool middleX = (x != 0 && x != size); bool middleY = (y != 0 && y != size); bool middleZ = (z != 0 && z != size); if (middleZ) { alphaTotal += (alpha4 = colorX[area] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } int alpha5 = alpha4, alpha6 = alpha4; if (middleY) { alphaTotal += (alpha2 = colorX[size] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleZ) { alphaTotal += (alpha6 = colorX[offset6] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } int alpha3 = alpha2, alpha7 = alpha6; if (middleX) { alphaTotal += (alpha1 = colorX[1] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleY) { alphaTotal += (alpha3 = colorX[offset3] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleZ) { alphaTotal += (alpha7 = colorX[offset7] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } if (middleZ) { alphaTotal += (alpha5 = colorX[offset5] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } if (alphaTotal == 0 || alphaTotal == possibleTotal) { if (x != 0) { colorX++; } continue; // no corners set/all corners set } // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded int clampedX = qMax(x - 1, 0), clampedY = qMax(y - 1, 0), clampedZ = qMax(z - 1, 0); const QRgb* hermiteBase = hermiteData + clampedZ * hermiteArea + clampedY * hermiteStride + clampedX * VoxelHermiteData::EDGE_COUNT; const char* materialBase = materialData ? (materialData + clampedZ * area + clampedY * size + clampedX) : NULL; int crossingCount = 0; if (middleX) { if (alpha0 != alpha1) { QRgb hermite = hermiteBase[0]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 0.0f); crossing.axis = 0; } if (middleY) { if (alpha1 != alpha3) { QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha1 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } else { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); crossing.axis = 1; } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } else { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 0.0f); crossing.axis = 0; } if (middleZ) { if (alpha3 != alpha7) { QRgb hermite = hermiteBase[hermiteStride + VoxelHermiteData::EDGE_COUNT + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha3 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } crossing.point = glm::vec3(1.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha5 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha5 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); crossing.axis = 1; } if (alpha6 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha6 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 1.0f); crossing.axis = 0; } } } if (middleZ) { if (alpha1 != alpha5) { QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha1 == 0) { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } else { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha4 != alpha5) { QRgb hermite = hermiteBase[hermiteArea]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha4 == 0) { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } else { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 1.0f); crossing.axis = 0; } } } if (middleY) { if (alpha0 != alpha2) { QRgb hermite = hermiteBase[1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); crossing.axis = 1; } if (middleZ) { if (alpha2 != alpha6) { QRgb hermite = hermiteBase[hermiteStride + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } else { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(0.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha4 != alpha6) { QRgb hermite = hermiteBase[hermiteArea + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha4 == 0) { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } else { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); crossing.axis = 1; } } } if (middleZ && alpha0 != alpha4) { QRgb hermite = hermiteBase[2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } // at present, we simply average the properties of each crossing as opposed to finding the vertex that // minimizes the quadratic error function as described in the reference paper glm::vec3 center; glm::vec3 axisNormals[3]; const int MAX_MATERIALS_PER_VERTEX = 4; quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; float totalWeight = 0.0f; int red = 0, green = 0, blue = 0; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; center += crossing.point; axisNormals[crossing.axis] += crossing.normal; red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); if (displayHermite) { glm::vec3 start = info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + crossing.point) * scale; hermiteSegments.append(start); hermiteSegments.append(start + crossing.normal * scale); } Lines 2001–2500 of 3173. Previous lines Load more
} if (_depth < _depthInfo.size()) { _depthInfo[_depth] = depthInfo; } else { _depthInfo.append(depthInfo); } if (!info.isLeaf) { return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; } postVisit(info); return STOP_RECURSION; } bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { 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<HeightfieldHeightDataPointer>(); 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<const HeightfieldBuffer*>( info.inputValues.at(3).getInlineValue<BufferDataPointer>().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); _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); } } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); } return true; } void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { regions.append(unextended); regionBounds.add(extended); } class HeightfieldUpdateVisitor : public MetavoxelVisitor { public: HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds); virtual int visit(MetavoxelInfo& info); private: const QVector<Box>& _regions; const Box& _regionBounds; QVector<Box> _intersections; HeightfieldFetchVisitor _fetchVisitor; }; HeightfieldUpdateVisitor::HeightfieldUpdateVisitor(const MetavoxelLOD& lod, const QVector<Box>& regions, const Box& regionBounds) : MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), _regions(regions), _regionBounds(regionBounds), _fetchVisitor(lod, _intersections) { } int HeightfieldUpdateVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_regionBounds)) { return STOP_RECURSION; } if (!info.isLeaf) { return DEFAULT_ORDER; } const HeightfieldBuffer* buffer = static_cast<const HeightfieldBuffer*>( info.inputValues.at(0).getInlineValue<BufferDataPointer>().data()); if (!buffer) { return STOP_RECURSION; } _intersections.clear(); foreach (const Box& region, _regions) { if (region.intersects(buffer->getHeightBounds())) { _intersections.append(region.getIntersection(buffer->getHeightBounds())); } } if (_intersections.isEmpty()) { return STOP_RECURSION; } HeightfieldBuffer* newBuffer = new HeightfieldBuffer(info.minimum, info.size, buffer->getHeight(), buffer->getColor(), buffer->getMaterial(), buffer->getMaterials()); _fetchVisitor.init(newBuffer); _data->guide(_fetchVisitor); BufferDataPointer pointer(newBuffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return STOP_RECURSION; } class VoxelAugmentVisitor : public MetavoxelVisitor { public: VoxelAugmentVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); }; VoxelAugmentVisitor::VoxelAugmentVisitor(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() << AttributeRegistry::getInstance()->getVoxelMaterialAttribute() << AttributeRegistry::getInstance()->getVoxelHermiteAttribute(), QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), lod) { } class EdgeCrossing { public: glm::vec3 point; glm::vec3 normal; QRgb color; char material; int axis; }; class AxisIndex { public: int x, y, z; AxisIndex(int x = -1, int y = -1, int z = -1) : x(x), y(y), z(z) { } }; static glm::vec3 safeNormalize(const glm::vec3& vector) { float length = glm::length(vector); return (length > 0.0f) ? (vector / length) : vector; } int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return DEFAULT_ORDER; } BufferData* buffer = NULL; VoxelColorDataPointer color = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>(); VoxelMaterialDataPointer material = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>(); VoxelHermiteDataPointer hermite = info.inputValues.at(2).getInlineValue<VoxelHermiteDataPointer>(); if (color && hermite) { QVector<VoxelPoint> vertices; QVector<int> indices; QVector<glm::vec3> hermiteSegments; QMultiHash<QRgb, int> quadIndices; // see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the // dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges const QVector<QRgb>& colorContents = color->getContents(); const QVector<QRgb>& hermiteContents = hermite->getContents(); int size = color->getSize(); int area = size * size; // number variables such as offset3 and alpha0 in this function correspond to cube corners, where the x, y, and z // components are represented as bits in the 0, 1, and 2 position, respectively; hence, alpha0 is the value at // the minimum x, y, and z corner and alpha7 is the value at the maximum x, y, and z int offset3 = size + 1; int offset5 = area + 1; int offset6 = area + size; int offset7 = area + size + 1; const QRgb* colorZ = colorContents.constData(); const QRgb* hermiteData = hermiteContents.constData(); int hermiteStride = hermite->getSize() * VoxelHermiteData::EDGE_COUNT; int hermiteArea = hermiteStride * hermite->getSize(); const char* materialData = material ? material->getContents().constData() : NULL; // as we scan down the cube generating vertices between grid points, we remember the indices of the last // (element, line, section--x, y, z) so that we can connect generated vertices as quads int expanded = size + 1; QVector<AxisIndex> lineIndices(expanded); QVector<AxisIndex> lastLineIndices(expanded); QVector<AxisIndex> planeIndices(expanded * expanded); QVector<AxisIndex> lastPlaneIndices(expanded * expanded); const int EDGES_PER_CUBE = 12; EdgeCrossing crossings[EDGES_PER_CUBE]; float highest = size - 1.0f; float scale = info.size / highest; const int ALPHA_OFFSET = 24; bool displayHermite = Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData); for (int z = 0; z < expanded; z++) { const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { AxisIndex lastIndex; const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { int alpha0 = colorX[0] >> ALPHA_OFFSET; int alpha1 = alpha0, alpha2 = alpha0, alpha4 = alpha0; int alphaTotal = alpha0; int possibleTotal = EIGHT_BIT_MAXIMUM; // cubes on the edge are two-dimensional: this ensures that their vertices will be shared between // neighboring blocks, which share only one layer of points bool middleX = (x != 0 && x != size); bool middleY = (y != 0 && y != size); bool middleZ = (z != 0 && z != size); if (middleZ) { alphaTotal += (alpha4 = colorX[area] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } int alpha5 = alpha4, alpha6 = alpha4; if (middleY) { alphaTotal += (alpha2 = colorX[size] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleZ) { alphaTotal += (alpha6 = colorX[offset6] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } int alpha3 = alpha2, alpha7 = alpha6; if (middleX) { alphaTotal += (alpha1 = colorX[1] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleY) { alphaTotal += (alpha3 = colorX[offset3] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; if (middleZ) { alphaTotal += (alpha7 = colorX[offset7] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } if (middleZ) { alphaTotal += (alpha5 = colorX[offset5] >> ALPHA_OFFSET); possibleTotal += EIGHT_BIT_MAXIMUM; } } if (alphaTotal == 0 || alphaTotal == possibleTotal) { if (x != 0) { colorX++; } continue; // no corners set/all corners set } // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded int clampedX = qMax(x - 1, 0), clampedY = qMax(y - 1, 0), clampedZ = qMax(z - 1, 0); const QRgb* hermiteBase = hermiteData + clampedZ * hermiteArea + clampedY * hermiteStride + clampedX * VoxelHermiteData::EDGE_COUNT; const char* materialBase = materialData ? (materialData + clampedZ * area + clampedY * size + clampedX) : NULL; int crossingCount = 0; if (middleX) { if (alpha0 != alpha1) { QRgb hermite = hermiteBase[0]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 0.0f); crossing.axis = 0; } if (middleY) { if (alpha1 != alpha3) { QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha1 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } else { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); crossing.axis = 1; } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } else { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 0.0f); crossing.axis = 0; } if (middleZ) { if (alpha3 != alpha7) { QRgb hermite = hermiteBase[hermiteStride + VoxelHermiteData::EDGE_COUNT + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha3 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset3]; crossing.material = materialBase ? materialBase[offset3] : 0; } crossing.point = glm::vec3(1.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha5 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + VoxelHermiteData::EDGE_COUNT + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha5 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); crossing.axis = 1; } if (alpha6 != alpha7) { QRgb hermite = hermiteBase[hermiteArea + hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha6 == 0) { crossing.color = colorX[offset7]; crossing.material = materialBase ? materialBase[offset7] : 0; } else { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f, 1.0f); crossing.axis = 0; } } } if (middleZ) { if (alpha1 != alpha5) { QRgb hermite = hermiteBase[VoxelHermiteData::EDGE_COUNT + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha1 == 0) { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } else { crossing.color = colorX[1]; crossing.material = materialBase ? materialBase[1] : 0; } crossing.point = glm::vec3(1.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha4 != alpha5) { QRgb hermite = hermiteBase[hermiteArea]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha4 == 0) { crossing.color = colorX[offset5]; crossing.material = materialBase ? materialBase[offset5] : 0; } else { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f, 1.0f); crossing.axis = 0; } } } if (middleY) { if (alpha0 != alpha2) { QRgb hermite = hermiteBase[1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); crossing.axis = 1; } if (middleZ) { if (alpha2 != alpha6) { QRgb hermite = hermiteBase[hermiteStride + 2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } else { crossing.color = colorX[size]; crossing.material = materialBase ? materialBase[size] : 0; } crossing.point = glm::vec3(0.0f, 1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } if (alpha4 != alpha6) { QRgb hermite = hermiteBase[hermiteArea + 1]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha4 == 0) { crossing.color = colorX[offset6]; crossing.material = materialBase ? materialBase[offset6] : 0; } else { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } crossing.point = glm::vec3(0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 1.0f); crossing.axis = 1; } } } if (middleZ && alpha0 != alpha4) { QRgb hermite = hermiteBase[2]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha0 == 0) { crossing.color = colorX[area]; crossing.material = materialBase ? materialBase[area] : 0; } else { crossing.color = colorX[0]; crossing.material = materialBase ? materialBase[0] : 0; } crossing.point = glm::vec3(0.0f, 0.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL); crossing.axis = 2; } // at present, we simply average the properties of each crossing as opposed to finding the vertex that // minimizes the quadratic error function as described in the reference paper glm::vec3 center; glm::vec3 axisNormals[3]; const int MAX_MATERIALS_PER_VERTEX = 4; quint8 materials[] = { 0, 0, 0, 0 }; glm::vec4 materialWeights; float totalWeight = 0.0f; int red = 0, green = 0, blue = 0; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; center += crossing.point; axisNormals[crossing.axis] += crossing.normal; red += qRed(crossing.color); green += qGreen(crossing.color); blue += qBlue(crossing.color); if (displayHermite) { glm::vec3 start = info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + crossing.point) * scale; hermiteSegments.append(start); hermiteSegments.append(start + crossing.normal * scale); }