} crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? 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); } 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); } 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); } 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); } } } 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); } 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); } } } 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); } 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); } 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); } } } 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); } // 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 normals[MAX_NORMALS_PER_VERTEX]; int normalCount = 0; const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); 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; int j = 0; for (; j < normalCount; j++) { if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { normals[j] = safeNormalize(normals[j] + crossing.normal); break; } } if (j == normalCount) { normals[normalCount++] = 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); } // when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { if (materials[j] == crossing.material) { materialWeights[j] += 1.0f; totalWeight += 1.0f; break; } else if (materials[j] == 0) { materials[j] = crossing.material; materialWeights[j] = 1.0f; totalWeight += 1.0f; break; } } } } center /= crossingCount; // use a sequence of Givens rotations to perform a QR decomposition // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf glm::mat4 r(0.0f); glm::vec4 bottom; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); for (int j = 0; j < 4; j++) { float angle = glm::atan(-bottom[j], r[j][j]); float sina = glm::sin(angle); float cosa = glm::cos(angle); for (int k = 0; k < 4; k++) { float tmp = bottom[k]; bottom[k] = sina * r[k][j] + cosa * tmp; r[k][j] = cosa * r[k][j] - sina * tmp; } } } // extract the submatrices, form ata glm::mat3 a(r); glm::vec3 b(r[3]); glm::mat3 atrans = glm::transpose(a); glm::mat3 ata = atrans * a; // find the eigenvalues and eigenvectors of ata // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) glm::mat3 d = ata; glm::quat combinedRotation; const int MAX_ITERATIONS = 20; for (int i = 0; i < MAX_ITERATIONS; i++) { glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); const float DESIRED_PRECISION = 0.00001f; if (offDiagonals[largestIndex] < DESIRED_PRECISION) { break; } int largestJ = (largestIndex == 2) ? 1 : 0; int largestI = (largestIndex == 0) ? 1 : 2; float sjj = d[largestJ][largestJ]; float sii = d[largestI][largestI]; float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); combinedRotation = glm::normalize(rotation * combinedRotation); glm::mat3 matrix = glm::mat3_cast(combinedRotation); d = matrix * ata * glm::transpose(matrix); } // form the singular matrix from the eigenvalues const float MIN_SINGULAR_THRESHOLD = 0.1f; d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; // compute the pseudo-inverse, ataplus, and use to find the minimizing solution glm::mat3 u = glm::mat3_cast(combinedRotation); glm::mat3 ataplus = glm::transpose(u) * d * u; glm::vec3 solution = (ataplus * atrans * b) + center; // make sure it doesn't fall beyond the cell boundaries center = glm::clamp(solution, 0.0f, 1.0f); if (totalWeight > 0.0f) { materialWeights *= (EIGHT_BIT_MAXIMUM / totalWeight); } VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; vertices.append(point); for (int i = 1; i < normalCount; i++) { index.indices[i] = vertices.size(); point.setNormal(normals[i]); vertices.append(point); } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating // quads for each edge that includes a transition, using indices of previously generated vertices if (x != 0 && y != 0 && z != 0) { if (alpha0 != alpha1) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); const NormalIndex& index1 = lastLineIndices.at(x); const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative x indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive x indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha2) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, vertices.at(index1.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative y indices.append(index1.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } else { // quad faces positive y indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha4) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastLineIndices.at(x - 1); const NormalIndex& index3 = lastLineIndices.at(x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative z indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive z indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } } lastIndex = index; lineIndices[x] = index; planeIndices[y * expanded + x] = index; if (x != 0) { colorX++; } } lineIndices.swap(lastLineIndices); if (y != 0) { colorY += size; } } planeIndices.swap(lastPlaneIndices); if (z != 0) { colorZ += area; } } buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size, material ? material->getMaterials() : QVector<SharedObjectPointer>()); } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return STOP_RECURSION;} void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod) { // copy the previous buffers MetavoxelData expandedPrevious = previous; while (expandedPrevious.getSize() < data.getSize()) { expandedPrevious.expand(); } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); MetavoxelNode* root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor);} class SpannerSimulateVisitor : public SpannerVisitor {public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual bool visit(Spanner* spanner); private: float _deltaTime;}; SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) {} bool SpannerSimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); return true;} void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod) { SpannerSimulateVisitor spannerSimulateVisitor(deltaTime, lod); data.guide(spannerSimulateVisitor);} class BufferRenderVisitor : public MetavoxelVisitor {public: BufferRenderVisitor(const AttributePointer& attribute); virtual int visit(MetavoxelInfo& info); private: int _order; int _containmentDepth;}; BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : MetavoxelVisitor(QVector<AttributePointer>() << attribute), _order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) {} int BufferRenderVisitor::visit(MetavoxelInfo& info) { if (_containmentDepth >= _depth) { Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( info.getBounds()); if (intersection == Frustum::NO_INTERSECTION) { return STOP_RECURSION; } _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; } if (!info.isLeaf) { return _order; } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>(); if (buffer) { buffer->render(); } return STOP_RECURSION;} void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSpanners)) { SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); }} SphereRenderer::SphereRenderer() {} void SphereRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Sphere* sphere = static_cast<Sphere*>(_spanner); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = sphere->getTranslation();Lines 1501–2000 of 2352. Previous lines Load more
} crossing.point = glm::vec3(1.0f, qAlpha(hermite) * EIGHT_BIT_MAXIMUM_RECIPROCAL, 0.0f); } if (alpha2 != alpha3) { QRgb hermite = hermiteBase[hermiteStride]; EdgeCrossing& crossing = crossings[crossingCount++]; crossing.normal = unpackNormal(hermite); if (alpha2 == 0) { crossing.color = colorX[offset3]; crossing.material = materialBase ? 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); } 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); } 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); } 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); } } } 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); } 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); } } } 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); } 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); } 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); } } } 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); } // 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 normals[MAX_NORMALS_PER_VERTEX]; int normalCount = 0; const float CREASE_COS_NORMAL = glm::cos(glm::radians(45.0f)); 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; int j = 0; for (; j < normalCount; j++) { if (glm::dot(normals[j], crossing.normal) > CREASE_COS_NORMAL) { normals[j] = safeNormalize(normals[j] + crossing.normal); break; } } if (j == normalCount) { normals[normalCount++] = 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); } // when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { for (j = 0; j < MAX_MATERIALS_PER_VERTEX; j++) { if (materials[j] == crossing.material) { materialWeights[j] += 1.0f; totalWeight += 1.0f; break; } else if (materials[j] == 0) { materials[j] = crossing.material; materialWeights[j] = 1.0f; totalWeight += 1.0f; break; } } } } center /= crossingCount; // use a sequence of Givens rotations to perform a QR decomposition // see http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf glm::mat4 r(0.0f); glm::vec4 bottom; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); for (int j = 0; j < 4; j++) { float angle = glm::atan(-bottom[j], r[j][j]); float sina = glm::sin(angle); float cosa = glm::cos(angle); for (int k = 0; k < 4; k++) { float tmp = bottom[k]; bottom[k] = sina * r[k][j] + cosa * tmp; r[k][j] = cosa * r[k][j] - sina * tmp; } } } // extract the submatrices, form ata glm::mat3 a(r); glm::vec3 b(r[3]); glm::mat3 atrans = glm::transpose(a); glm::mat3 ata = atrans * a; // find the eigenvalues and eigenvectors of ata // (see http://en.wikipedia.org/wiki/Jacobi_eigenvalue_algorithm) glm::mat3 d = ata; glm::quat combinedRotation; const int MAX_ITERATIONS = 20; for (int i = 0; i < MAX_ITERATIONS; i++) { glm::vec3 offDiagonals = glm::abs(glm::vec3(d[1][0], d[2][0], d[2][1])); int largestIndex = (offDiagonals[0] > offDiagonals[1]) ? (offDiagonals[0] > offDiagonals[2] ? 0 : 2) : (offDiagonals[1] > offDiagonals[2] ? 1 : 2); const float DESIRED_PRECISION = 0.00001f; if (offDiagonals[largestIndex] < DESIRED_PRECISION) { break; } int largestJ = (largestIndex == 2) ? 1 : 0; int largestI = (largestIndex == 0) ? 1 : 2; float sjj = d[largestJ][largestJ]; float sii = d[largestI][largestI]; float angle = glm::atan(2.0f * d[largestJ][largestI], sjj - sii) / 2.0f; glm::quat rotation = glm::angleAxis(angle, largestIndex == 0 ? glm::vec3(0.0f, 0.0f, -1.0f) : (largestIndex == 1 ? glm::vec3(0.0f, 1.0f, 0.0f) : glm::vec3(-1.0f, 0.0f, 0.0f))); combinedRotation = glm::normalize(rotation * combinedRotation); glm::mat3 matrix = glm::mat3_cast(combinedRotation); d = matrix * ata * glm::transpose(matrix); } // form the singular matrix from the eigenvalues const float MIN_SINGULAR_THRESHOLD = 0.1f; d[0][0] = (d[0][0] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[0][0]; d[1][1] = (d[1][1] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[1][1]; d[2][2] = (d[2][2] < MIN_SINGULAR_THRESHOLD) ? 0.0f : 1.0f / d[2][2]; // compute the pseudo-inverse, ataplus, and use to find the minimizing solution glm::mat3 u = glm::mat3_cast(combinedRotation); glm::mat3 ataplus = glm::transpose(u) * d * u; glm::vec3 solution = (ataplus * atrans * b) + center; // make sure it doesn't fall beyond the cell boundaries center = glm::clamp(solution, 0.0f, 1.0f); if (totalWeight > 0.0f) { materialWeights *= (EIGHT_BIT_MAXIMUM / totalWeight); } VoxelPoint point = { info.minimum + (glm::vec3(clampedX, clampedY, clampedZ) + center) * scale, { (quint8)(red / crossingCount), (quint8)(green / crossingCount), (quint8)(blue / crossingCount) }, { (char)(normals[0].x * 127.0f), (char)(normals[0].y * 127.0f), (char)(normals[0].z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; NormalIndex index = { { vertices.size(), vertices.size(), vertices.size(), vertices.size() } }; vertices.append(point); for (int i = 1; i < normalCount; i++) { index.indices[i] = vertices.size(); point.setNormal(normals[i]); vertices.append(point); } // the first x, y, and z are repeated for the boundary edge; past that, we consider generating // quads for each edge that includes a transition, using indices of previously generated vertices if (x != 0 && y != 0 && z != 0) { if (alpha0 != alpha1) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); const NormalIndex& index1 = lastLineIndices.at(x); const NormalIndex& index2 = lastPlaneIndices.at((y - 1) * expanded + x); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative x indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive x indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha2) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size()); quadIndices.insert(qRgb(x, y, z - 1), indices.size()); const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastPlaneIndices.at(y * expanded + x - 1); const NormalIndex& index3 = lastPlaneIndices.at(y * expanded + x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, vertices.at(index1.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative y indices.append(index1.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } else { // quad faces positive y indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } if (alpha0 != alpha4) { quadIndices.insert(qRgb(x, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y, z), indices.size()); quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size()); quadIndices.insert(qRgb(x, y - 1, z), indices.size()); const NormalIndex& index1 = lastIndex; const NormalIndex& index2 = lastLineIndices.at(x - 1); const NormalIndex& index3 = lastLineIndices.at(x); const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, vertices.at(index3.indices[0]).vertex - first); if (alpha0 == 0) { // quad faces negative z indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices)); } else { // quad faces positive z indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices)); } indices.append(index.getClosestIndex(normal, vertices)); } } lastIndex = index; lineIndices[x] = index; planeIndices[y * expanded + x] = index; if (x != 0) { colorX++; } } lineIndices.swap(lastLineIndices); if (y != 0) { colorY += size; } } planeIndices.swap(lastPlaneIndices); if (z != 0) { colorZ += area; } } buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size, material ? material->getMaterials() : QVector<SharedObjectPointer>()); } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return STOP_RECURSION; } void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod) { // copy the previous buffers MetavoxelData expandedPrevious = previous; while (expandedPrevious.getSize() < data.getSize()) { expandedPrevious.expand(); } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); MetavoxelNode* root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); } class SpannerSimulateVisitor : public SpannerVisitor { public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual bool visit(Spanner* spanner); private: float _deltaTime; }; SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) { } bool SpannerSimulateVisitor::visit(Spanner* spanner) { spanner->getRenderer()->simulate(_deltaTime); return true; } void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod) { SpannerSimulateVisitor spannerSimulateVisitor(deltaTime, lod); data.guide(spannerSimulateVisitor); } class BufferRenderVisitor : public MetavoxelVisitor { public: BufferRenderVisitor(const AttributePointer& attribute); virtual int visit(MetavoxelInfo& info); private: int _order; int _containmentDepth; }; BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : MetavoxelVisitor(QVector<AttributePointer>() << attribute), _order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } int BufferRenderVisitor::visit(MetavoxelInfo& info) { if (_containmentDepth >= _depth) { Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType( info.getBounds()); if (intersection == Frustum::NO_INTERSECTION) { return STOP_RECURSION; } _containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX; } if (!info.isLeaf) { return _order; } BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>(); if (buffer) { buffer->render(); } return STOP_RECURSION; } void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSpanners)) { SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); } } SphereRenderer::SphereRenderer() { } void SphereRenderer::render(const MetavoxelLOD& lod, bool contained, bool cursor) { Sphere* sphere = static_cast<Sphere*>(_spanner); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glPushMatrix(); const glm::vec3& translation = sphere->getTranslation();