} void Augmenter::run() { SharedNodePointer node = _node; if (!node) { return; } AugmentVisitor visitor(_lod, _previousData); _data.guide(visitor); QMutexLocker locker(&node->getMutex()); QMetaObject::invokeMethod(node->getLinkedData(), "setAugmentedData", Q_ARG(const MetavoxelData&, _data));} void MetavoxelSystemClient::refreshVoxelData() { // make it look as if all the colors have changed MetavoxelData oldData = getAugmentedData(); oldData.touch(AttributeRegistry::getInstance()->getVoxelColorAttribute()); QThreadPool::globalInstance()->start(new Augmenter(_node, _data, oldData, _remoteDataLOD));} void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { MetavoxelClient::dataChanged(oldData); QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD));} class SendDelayer : public QObject {public: SendDelayer(const SharedNodePointer& node, const QByteArray& data); virtual void timerEvent(QTimerEvent* event); private: SharedNodePointer _node; QByteArray _data;}; SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : _node(node), _data(data.constData(), data.size()) {} void SendDelayer::timerEvent(QTimerEvent* event) { NodeList::getInstance()->writeDatagram(_data, _node); deleteLater();} void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); if (randFloat() < simulation.dropRate) { return; } int count = (randFloat() < simulation.repeatRate) ? 2 : 1; for (int i = 0; i < count; i++) { if (simulation.bandwidthLimit > 0) { _sendThrottle.setLimit(simulation.bandwidthLimit); if (_sendThrottle.shouldThrottle(data.size())) { continue; } } int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); if (delay > 0) { SendDelayer* delayer = new SendDelayer(_node, data); delayer->startTimer(delay); } else { NodeList::getInstance()->writeDatagram(data, _node); } Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); }} BufferData::~BufferData() {} void VoxelPoint::setNormal(const glm::vec3& normal) { this->normal[0] = (char)(normal.x * 127.0f); this->normal[1] = (char)(normal.y * 127.0f); this->normal[2] = (char)(normal.z * 127.0f);} VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite, const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), _quadIndices(quadIndices), _size(size), _vertexCount(vertices.size()), _indexCount(indices.size()), _hermiteCount(hermite.size()), _indexBuffer(QOpenGLBuffer::IndexBuffer), _materials(materials) {} static bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) { glm::vec3 firstSide = v0 - v1; glm::vec3 secondSide = v2 - v1; glm::vec3 normal = glm::cross(secondSide, firstSide); float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); if (dividend > 0.0f) { return false; // origin below plane } float divisor = glm::dot(normal, direction); if (divisor > -EPSILON) { return false; } float t = dividend / divisor; glm::vec3 point = origin + direction * t; if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { distance = t; return true; } return false;} bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, const glm::vec3& direction, float& distance) const { float highest = _size - 1.0f; glm::vec3 position = entry * highest; glm::vec3 floors = glm::floor(position); int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); forever { for (QMultiHash<VoxelCoord, int>::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); it != _quadIndices.constEnd(); it++) { const int* indices = _indices.constData() + *it; if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, _vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) || findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, _vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) { return true; } } float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX; if (direction.x > 0.0f) { xDistance = (x + 1.0f - position.x) / direction.x; } else if (direction.x < 0.0f) { xDistance = (x - position.x) / direction.x; } if (direction.y > 0.0f) { yDistance = (y + 1.0f - position.y) / direction.y; } else if (direction.y < 0.0f) { yDistance = (y - position.y) / direction.y; } if (direction.z > 0.0f) { zDistance = (z + 1.0f - position.z) / direction.z; } else if (direction.z < 0.0f) { zDistance = (z - position.z) / direction.z; } float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance)); if (minimumDistance == xDistance) { if (direction.x > 0.0f) { if (x++ == max) { return false; } } else if (x-- == 0) { return false; } } if (minimumDistance == yDistance) { if (direction.y > 0.0f) { if (y++ == max) { return false; } } else if (y-- == 0) { return false; } } if (minimumDistance == zDistance) { if (direction.z > 0.0f) { if (z++ == max) { return false; } } else if (z-- == 0) { return false; } } position += direction * minimumDistance; } return false;} void VoxelBuffer::render(bool cursor) { if (!_vertexBuffer.isCreated()) { _vertexBuffer.create(); _vertexBuffer.bind(); _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); _vertexBuffer.release(); _indexBuffer.create(); _indexBuffer.bind(); _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); _indexBuffer.release(); if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); for (int i = 0; i < _materials.size(); i++) { const SharedObjectPointer material = _materials.at(i); if (material) { _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( static_cast<MaterialObject*>(material.data())->getDiffuse(), SPLAT_TEXTURE); } } } } if (cursor) { _vertexBuffer.bind(); _indexBuffer.bind(); VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); _vertexBuffer.release(); _indexBuffer.release(); return; } VoxelBatch baseBatch; baseBatch.vertexBuffer = &_vertexBuffer; baseBatch.indexBuffer = &_indexBuffer; baseBatch.vertexCount = _vertexCount; baseBatch.indexCount = _indexCount; Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); if (!_materials.isEmpty()) { VoxelSplatBatch splatBatch; splatBatch.vertexBuffer = &_vertexBuffer; splatBatch.indexBuffer = &_indexBuffer; splatBatch.vertexCount = _vertexCount; splatBatch.indexCount = _indexCount; for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { for (int j = 0; j < SPLAT_COUNT; j++) { int index = i + j; if (index < _networkTextures.size()) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast<MaterialObject*>(_materials.at(index).data()); splatBatch.splatTextureScalesS[j] = 1.0f / material->getScaleS(); splatBatch.splatTextureScalesT[j] = 1.0f / material->getScaleT(); splatBatch.splatTextureIDs[j] = texture->getID(); } else { splatBatch.splatTextureIDs[j] = 0; } } else { splatBatch.splatTextureIDs[j] = 0; } } splatBatch.materialIndex = i; Application::getInstance()->getMetavoxels()->addVoxelSplatBatch(splatBatch); } } if (_hermiteCount > 0) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); _hermiteBuffer.allocate(_hermite.constData(), _hermite.size() * sizeof(glm::vec3)); _hermiteBuffer.release(); _hermite.clear(); } HermiteBatch hermiteBatch; hermiteBatch.vertexBuffer = &_hermiteBuffer; hermiteBatch.vertexCount = _hermiteCount; Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); }} BufferDataAttribute::BufferDataAttribute(const QString& name) : InlineAttribute<BufferDataPointer>(name) {} bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const { *(BufferDataPointer*)&parent = _defaultValue; for (int i = 0; i < MERGE_COUNT; i++) { if (decodeInline<BufferDataPointer>(children[i])) { return false; } } return true;} AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const { return AttributeValue(parentValue.getAttribute());} DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() {} 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;}; const int MAX_NORMALS_PER_VERTEX = 4; class NormalIndex {public: int indices[MAX_NORMALS_PER_VERTEX]; int getClosestIndex(const glm::vec3& normal, QVector<VoxelPoint>& vertices) const;}; int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector<VoxelPoint>& vertices) const { int firstIndex = indices[0]; int closestIndex = firstIndex; const VoxelPoint& firstVertex = vertices.at(firstIndex); float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { int index = indices[i]; if (index == firstIndex) { break; } const VoxelPoint& vertex = vertices.at(index); float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; if (product > closest) { closest = product; closestIndex = index; } } return closestIndex;} 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<VoxelCoord, 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<NormalIndex> lineIndices(expanded); QVector<NormalIndex> lastLineIndices(expanded); QVector<NormalIndex> planeIndices(expanded * expanded); QVector<NormalIndex> 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++) { NormalIndex 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); } 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;Lines 1001–1500 of 2352. Previous lines Load more
} void Augmenter::run() { SharedNodePointer node = _node; if (!node) { return; } AugmentVisitor visitor(_lod, _previousData); _data.guide(visitor); QMutexLocker locker(&node->getMutex()); QMetaObject::invokeMethod(node->getLinkedData(), "setAugmentedData", Q_ARG(const MetavoxelData&, _data)); } void MetavoxelSystemClient::refreshVoxelData() { // make it look as if all the colors have changed MetavoxelData oldData = getAugmentedData(); oldData.touch(AttributeRegistry::getInstance()->getVoxelColorAttribute()); QThreadPool::globalInstance()->start(new Augmenter(_node, _data, oldData, _remoteDataLOD)); } void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { MetavoxelClient::dataChanged(oldData); QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); } class SendDelayer : public QObject { public: SendDelayer(const SharedNodePointer& node, const QByteArray& data); virtual void timerEvent(QTimerEvent* event); private: SharedNodePointer _node; QByteArray _data; }; SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : _node(node), _data(data.constData(), data.size()) { } void SendDelayer::timerEvent(QTimerEvent* event) { NodeList::getInstance()->writeDatagram(_data, _node); deleteLater(); } void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); if (randFloat() < simulation.dropRate) { return; } int count = (randFloat() < simulation.repeatRate) ? 2 : 1; for (int i = 0; i < count; i++) { if (simulation.bandwidthLimit > 0) { _sendThrottle.setLimit(simulation.bandwidthLimit); if (_sendThrottle.shouldThrottle(data.size())) { continue; } } int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); if (delay > 0) { SendDelayer* delayer = new SendDelayer(_node, data); delayer->startTimer(delay); } else { NodeList::getInstance()->writeDatagram(data, _node); } Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); } } BufferData::~BufferData() { } void VoxelPoint::setNormal(const glm::vec3& normal) { this->normal[0] = (char)(normal.x * 127.0f); this->normal[1] = (char)(normal.y * 127.0f); this->normal[2] = (char)(normal.z * 127.0f); } VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite, const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials) : _vertices(vertices), _indices(indices), _hermite(hermite), _quadIndices(quadIndices), _size(size), _vertexCount(vertices.size()), _indexCount(indices.size()), _hermiteCount(hermite.size()), _indexBuffer(QOpenGLBuffer::IndexBuffer), _materials(materials) { } static bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) { glm::vec3 firstSide = v0 - v1; glm::vec3 secondSide = v2 - v1; glm::vec3 normal = glm::cross(secondSide, firstSide); float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); if (dividend > 0.0f) { return false; // origin below plane } float divisor = glm::dot(normal, direction); if (divisor > -EPSILON) { return false; } float t = dividend / divisor; glm::vec3 point = origin + direction * t; if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { distance = t; return true; } return false; } bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin, const glm::vec3& direction, float& distance) const { float highest = _size - 1.0f; glm::vec3 position = entry * highest; glm::vec3 floors = glm::floor(position); int max = _size - 2; int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max); forever { for (QMultiHash<VoxelCoord, int>::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1)); it != _quadIndices.constEnd(); it++) { const int* indices = _indices.constData() + *it; if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, _vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) || findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex, _vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) { return true; } } float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX; if (direction.x > 0.0f) { xDistance = (x + 1.0f - position.x) / direction.x; } else if (direction.x < 0.0f) { xDistance = (x - position.x) / direction.x; } if (direction.y > 0.0f) { yDistance = (y + 1.0f - position.y) / direction.y; } else if (direction.y < 0.0f) { yDistance = (y - position.y) / direction.y; } if (direction.z > 0.0f) { zDistance = (z + 1.0f - position.z) / direction.z; } else if (direction.z < 0.0f) { zDistance = (z - position.z) / direction.z; } float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance)); if (minimumDistance == xDistance) { if (direction.x > 0.0f) { if (x++ == max) { return false; } } else if (x-- == 0) { return false; } } if (minimumDistance == yDistance) { if (direction.y > 0.0f) { if (y++ == max) { return false; } } else if (y-- == 0) { return false; } } if (minimumDistance == zDistance) { if (direction.z > 0.0f) { if (z++ == max) { return false; } } else if (z-- == 0) { return false; } } position += direction * minimumDistance; } return false; } void VoxelBuffer::render(bool cursor) { if (!_vertexBuffer.isCreated()) { _vertexBuffer.create(); _vertexBuffer.bind(); _vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint)); _vertexBuffer.release(); _indexBuffer.create(); _indexBuffer.bind(); _indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int)); _indexBuffer.release(); if (!_materials.isEmpty()) { _networkTextures.resize(_materials.size()); for (int i = 0; i < _materials.size(); i++) { const SharedObjectPointer material = _materials.at(i); if (material) { _networkTextures[i] = Application::getInstance()->getTextureCache()->getTexture( static_cast<MaterialObject*>(material.data())->getDiffuse(), SPLAT_TEXTURE); } } } } if (cursor) { _vertexBuffer.bind(); _indexBuffer.bind(); VoxelPoint* point = 0; glVertexPointer(3, GL_FLOAT, sizeof(VoxelPoint), &point->vertex); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(VoxelPoint), &point->normal); glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0); _vertexBuffer.release(); _indexBuffer.release(); return; } VoxelBatch baseBatch; baseBatch.vertexBuffer = &_vertexBuffer; baseBatch.indexBuffer = &_indexBuffer; baseBatch.vertexCount = _vertexCount; baseBatch.indexCount = _indexCount; Application::getInstance()->getMetavoxels()->addVoxelBaseBatch(baseBatch); if (!_materials.isEmpty()) { VoxelSplatBatch splatBatch; splatBatch.vertexBuffer = &_vertexBuffer; splatBatch.indexBuffer = &_indexBuffer; splatBatch.vertexCount = _vertexCount; splatBatch.indexCount = _indexCount; for (int i = 0; i < _materials.size(); i += SPLAT_COUNT) { for (int j = 0; j < SPLAT_COUNT; j++) { int index = i + j; if (index < _networkTextures.size()) { const NetworkTexturePointer& texture = _networkTextures.at(index); if (texture) { MaterialObject* material = static_cast<MaterialObject*>(_materials.at(index).data()); splatBatch.splatTextureScalesS[j] = 1.0f / material->getScaleS(); splatBatch.splatTextureScalesT[j] = 1.0f / material->getScaleT(); splatBatch.splatTextureIDs[j] = texture->getID(); } else { splatBatch.splatTextureIDs[j] = 0; } } else { splatBatch.splatTextureIDs[j] = 0; } } splatBatch.materialIndex = i; Application::getInstance()->getMetavoxels()->addVoxelSplatBatch(splatBatch); } } if (_hermiteCount > 0) { if (!_hermiteBuffer.isCreated()) { _hermiteBuffer.create(); _hermiteBuffer.bind(); _hermiteBuffer.allocate(_hermite.constData(), _hermite.size() * sizeof(glm::vec3)); _hermiteBuffer.release(); _hermite.clear(); } HermiteBatch hermiteBatch; hermiteBatch.vertexBuffer = &_hermiteBuffer; hermiteBatch.vertexCount = _hermiteCount; Application::getInstance()->getMetavoxels()->addHermiteBatch(hermiteBatch); } } BufferDataAttribute::BufferDataAttribute(const QString& name) : InlineAttribute<BufferDataPointer>(name) { } bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const { *(BufferDataPointer*)&parent = _defaultValue; for (int i = 0; i < MERGE_COUNT; i++) { if (decodeInline<BufferDataPointer>(children[i])) { return false; } } return true; } AttributeValue BufferDataAttribute::inherit(const AttributeValue& parentValue) const { return AttributeValue(parentValue.getAttribute()); } DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() { } 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; }; const int MAX_NORMALS_PER_VERTEX = 4; class NormalIndex { public: int indices[MAX_NORMALS_PER_VERTEX]; int getClosestIndex(const glm::vec3& normal, QVector<VoxelPoint>& vertices) const; }; int NormalIndex::getClosestIndex(const glm::vec3& normal, QVector<VoxelPoint>& vertices) const { int firstIndex = indices[0]; int closestIndex = firstIndex; const VoxelPoint& firstVertex = vertices.at(firstIndex); float closest = normal.x * firstVertex.normal[0] + normal.y * firstVertex.normal[1] + normal.z * firstVertex.normal[2]; for (int i = 1; i < MAX_NORMALS_PER_VERTEX; i++) { int index = indices[i]; if (index == firstIndex) { break; } const VoxelPoint& vertex = vertices.at(index); float product = normal.x * vertex.normal[0] + normal.y * vertex.normal[1] + normal.z * vertex.normal[2]; if (product > closest) { closest = product; closestIndex = index; } } return closestIndex; } 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<VoxelCoord, 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<NormalIndex> lineIndices(expanded); QVector<NormalIndex> lastLineIndices(expanded); QVector<NormalIndex> planeIndices(expanded * expanded); QVector<NormalIndex> 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++) { NormalIndex 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); } 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;