// when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { for (int 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; } } } } glm::vec3 normal = safeNormalize(axisNormals[0] + axisNormals[1] + axisNormals[2]); 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; float smallestCosNormal = 1.0f; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); smallestCosNormal = qMin(smallestCosNormal, glm::dot(crossing.normal, normal)); 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)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; // determine whether we must "crease" by generating directional normals const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); AxisIndex index(vertices.size(), vertices.size(), vertices.size()); if (smallestCosNormal > CREASE_COS_NORMAL) { vertices.append(point); } else { axisNormals[0] = safeNormalize(axisNormals[0]); axisNormals[1] = safeNormalize(axisNormals[1]); axisNormals[2] = safeNormalize(axisNormals[2]); glm::vec3 normalXY(safeNormalize(axisNormals[0] + axisNormals[1])); glm::vec3 normalXZ(safeNormalize(axisNormals[0] + axisNormals[2])); glm::vec3 normalYZ(safeNormalize(axisNormals[1] + axisNormals[2])); if (glm::dot(axisNormals[0], normalXY) > CREASE_COS_NORMAL && glm::dot(axisNormals[1], normalXY) > CREASE_COS_NORMAL) { point.setNormal(normalXY); vertices.append(point); point.setNormal(axisNormals[2]); index.z = vertices.size(); vertices.append(point); } else if (glm::dot(axisNormals[0], normalXZ) > CREASE_COS_NORMAL && glm::dot(axisNormals[2], normalXZ) > CREASE_COS_NORMAL) { point.setNormal(normalXZ); vertices.append(point); point.setNormal(axisNormals[1]); index.y = vertices.size(); vertices.append(point); } else if (glm::dot(axisNormals[1], normalYZ) > CREASE_COS_NORMAL && glm::dot(axisNormals[2], normalYZ) > CREASE_COS_NORMAL) { point.setNormal(normalYZ); vertices.append(point); point.setNormal(axisNormals[0]); index.x = vertices.size(); vertices.append(point); } else { point.setNormal(axisNormals[0]); vertices.append(point); point.setNormal(axisNormals[1]); index.y = vertices.size(); vertices.append(point); point.setNormal(axisNormals[2]); index.z = vertices.size(); 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()); indices.append(index.x); int index1 = lastLineIndices.at(x).x; int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x; int index3 = lastPlaneIndices.at(y * expanded + x).x; if (alpha0 == 0) { // quad faces negative x indices.append(index3); indices.append(index2); indices.append(index1); } else { // quad faces positive x indices.append(index1); indices.append(index2); indices.append(index3); } } 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()); indices.append(index.y); int index1 = lastIndex.y; int index2 = lastPlaneIndices.at(y * expanded + x - 1).y; int index3 = lastPlaneIndices.at(y * expanded + x).y; if (alpha0 == 0) { // quad faces negative y indices.append(index1); indices.append(index2); indices.append(index3); } else { // quad faces positive y indices.append(index3); indices.append(index2); indices.append(index1); } } 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()); indices.append(index.z); int index1 = lastIndex.z; int index2 = lastLineIndices.at(x - 1).z; int index3 = lastLineIndices.at(x).z; if (alpha0 == 0) { // quad faces negative z indices.append(index3); indices.append(index2); indices.append(index1); } else { // quad faces positive z indices.append(index1); indices.append(index2); indices.append(index3); } } } 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& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); MetavoxelNode* root = expandedPrevious.getRoot(pointBufferAttribute); if (root) { data.setRoot(pointBufferAttribute, root); root->incrementReferenceCount(); } const AttributePointer& heightfieldBufferAttribute = Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); root = expandedPrevious.getRoot(heightfieldBufferAttribute); if (root) { data.setRoot(heightfieldBufferAttribute, root); root->incrementReferenceCount(); } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } PointAugmentVisitor pointAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, pointAugmentVisitor); HeightfieldRegionVisitor heightfieldRegionVisitor(lod); data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, heightfieldRegionVisitor.regionBounds); data.guide(heightfieldUpdateVisitor); VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor);} class SpannerSimulateVisitor : public SpannerVisitor {public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); private: float _deltaTime;}; SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) {} bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { 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 SpannerRenderVisitor : public SpannerVisitor {public: SpannerRenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); private: int _containmentDepth;}; SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod, encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) {} int SpannerRenderVisitor::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; } return SpannerVisitor::visit(info);} bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); return true;} 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) { SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int VIEWPORT_WIDTH_INDEX = 2; const int VIEWPORT_HEIGHT_INDEX = 3; float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight); float worldDiagonal = glm::distance(Application::getInstance()->getDisplayViewFrustum()->getNearBottomLeft(), Application::getInstance()->getDisplayViewFrustum()->getNearTopRight()); _pointProgram.bind(); _pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal * Application::getInstance()->getDisplayViewFrustum()->getNearClip() / worldDiagonal); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisable(GL_BLEND); BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); data.guide(pointRenderVisitor); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); _pointProgram.release(); Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); glEnable(GL_CULL_FACE); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_EQUAL, 0.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { _baseHeightfieldProgram.bind(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); data.guide(heightfieldRenderVisitor); glDisableClientState(GL_TEXTURE_COORD_ARRAY); _baseHeightfieldProgram.release(); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); _baseVoxelProgram.bind(); BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); _baseVoxelProgram.release(); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);} void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations) { program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_" + type + "_splat.vert"); program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/metavoxel_" + type + "_splat.frag"); program.link(); program.bind(); program.setUniformValue("heightMap", 0); program.setUniformValue("textureMap", 1); program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); locations.heightScale = program.uniformLocation("heightScale"); locations.textureScale = program.uniformLocation("textureScale"); locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); locations.textureValueMinima = program.uniformLocation("textureValueMinima"); locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); locations.materials = program.attributeLocation("materials"); locations.materialWeights = program.attributeLocation("materialWeights"); program.release();} ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;int DefaultMetavoxelRendererImplementation::_pointScaleLocation;ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram;int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation;Lines 2501–3000 of 3173. Previous lines Load more
// when assigning a material, search for its presence and, if not found, // place it in the first empty slot if (crossing.material != 0) { for (int 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; } } } } glm::vec3 normal = safeNormalize(axisNormals[0] + axisNormals[1] + axisNormals[2]); 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; float smallestCosNormal = 1.0f; for (int i = 0; i < crossingCount; i++) { const EdgeCrossing& crossing = crossings[i]; bottom = glm::vec4(crossing.normal, glm::dot(crossing.normal, crossing.point - center)); smallestCosNormal = qMin(smallestCosNormal, glm::dot(crossing.normal, normal)); 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)(normal.x * 127.0f), (char)(normal.y * 127.0f), (char)(normal.z * 127.0f) }, { materials[0], materials[1], materials[2], materials[3] }, { (quint8)materialWeights[0], (quint8)materialWeights[1], (quint8)materialWeights[2], (quint8)materialWeights[3] } }; // determine whether we must "crease" by generating directional normals const float CREASE_COS_NORMAL = glm::cos(glm::radians(40.0f)); AxisIndex index(vertices.size(), vertices.size(), vertices.size()); if (smallestCosNormal > CREASE_COS_NORMAL) { vertices.append(point); } else { axisNormals[0] = safeNormalize(axisNormals[0]); axisNormals[1] = safeNormalize(axisNormals[1]); axisNormals[2] = safeNormalize(axisNormals[2]); glm::vec3 normalXY(safeNormalize(axisNormals[0] + axisNormals[1])); glm::vec3 normalXZ(safeNormalize(axisNormals[0] + axisNormals[2])); glm::vec3 normalYZ(safeNormalize(axisNormals[1] + axisNormals[2])); if (glm::dot(axisNormals[0], normalXY) > CREASE_COS_NORMAL && glm::dot(axisNormals[1], normalXY) > CREASE_COS_NORMAL) { point.setNormal(normalXY); vertices.append(point); point.setNormal(axisNormals[2]); index.z = vertices.size(); vertices.append(point); } else if (glm::dot(axisNormals[0], normalXZ) > CREASE_COS_NORMAL && glm::dot(axisNormals[2], normalXZ) > CREASE_COS_NORMAL) { point.setNormal(normalXZ); vertices.append(point); point.setNormal(axisNormals[1]); index.y = vertices.size(); vertices.append(point); } else if (glm::dot(axisNormals[1], normalYZ) > CREASE_COS_NORMAL && glm::dot(axisNormals[2], normalYZ) > CREASE_COS_NORMAL) { point.setNormal(normalYZ); vertices.append(point); point.setNormal(axisNormals[0]); index.x = vertices.size(); vertices.append(point); } else { point.setNormal(axisNormals[0]); vertices.append(point); point.setNormal(axisNormals[1]); index.y = vertices.size(); vertices.append(point); point.setNormal(axisNormals[2]); index.z = vertices.size(); 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()); indices.append(index.x); int index1 = lastLineIndices.at(x).x; int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x; int index3 = lastPlaneIndices.at(y * expanded + x).x; if (alpha0 == 0) { // quad faces negative x indices.append(index3); indices.append(index2); indices.append(index1); } else { // quad faces positive x indices.append(index1); indices.append(index2); indices.append(index3); } } 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()); indices.append(index.y); int index1 = lastIndex.y; int index2 = lastPlaneIndices.at(y * expanded + x - 1).y; int index3 = lastPlaneIndices.at(y * expanded + x).y; if (alpha0 == 0) { // quad faces negative y indices.append(index1); indices.append(index2); indices.append(index3); } else { // quad faces positive y indices.append(index3); indices.append(index2); indices.append(index1); } } 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()); indices.append(index.z); int index1 = lastIndex.z; int index2 = lastLineIndices.at(x - 1).z; int index3 = lastLineIndices.at(x).z; if (alpha0 == 0) { // quad faces negative z indices.append(index3); indices.append(index2); indices.append(index1); } else { // quad faces positive z indices.append(index1); indices.append(index2); indices.append(index3); } } } 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& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); MetavoxelNode* root = expandedPrevious.getRoot(pointBufferAttribute); if (root) { data.setRoot(pointBufferAttribute, root); root->incrementReferenceCount(); } const AttributePointer& heightfieldBufferAttribute = Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(); root = expandedPrevious.getRoot(heightfieldBufferAttribute); if (root) { data.setRoot(heightfieldBufferAttribute, root); root->incrementReferenceCount(); } const AttributePointer& voxelBufferAttribute = Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(); root = expandedPrevious.getRoot(voxelBufferAttribute); if (root) { data.setRoot(voxelBufferAttribute, root); root->incrementReferenceCount(); } PointAugmentVisitor pointAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, pointAugmentVisitor); HeightfieldRegionVisitor heightfieldRegionVisitor(lod); data.guideToDifferent(expandedPrevious, heightfieldRegionVisitor); HeightfieldUpdateVisitor heightfieldUpdateVisitor(lod, heightfieldRegionVisitor.regions, heightfieldRegionVisitor.regionBounds); data.guide(heightfieldUpdateVisitor); VoxelAugmentVisitor voxelAugmentVisitor(lod); data.guideToDifferent(expandedPrevious, voxelAugmentVisitor); } class SpannerSimulateVisitor : public SpannerVisitor { public: SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod); virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); private: float _deltaTime; }; SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod), _deltaTime(deltaTime) { } bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { 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 SpannerRenderVisitor : public SpannerVisitor { public: SpannerRenderVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize); private: int _containmentDepth; }; SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod, encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } int SpannerRenderVisitor::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; } return SpannerVisitor::visit(info); } bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { const glm::vec4 OPAQUE_WHITE(1.0f, 1.0f, 1.0f, 1.0f); spanner->getRenderer()->render(OPAQUE_WHITE, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); return true; } 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) { SpannerRenderVisitor spannerRenderVisitor(lod); data.guide(spannerRenderVisitor); int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int VIEWPORT_WIDTH_INDEX = 2; const int VIEWPORT_HEIGHT_INDEX = 3; float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight); float worldDiagonal = glm::distance(Application::getInstance()->getDisplayViewFrustum()->getNearBottomLeft(), Application::getInstance()->getDisplayViewFrustum()->getNearTopRight()); _pointProgram.bind(); _pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal * Application::getInstance()->getDisplayViewFrustum()->getNearClip() / worldDiagonal); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisable(GL_BLEND); BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute()); data.guide(pointRenderVisitor); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); _pointProgram.release(); Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, true); glEnable(GL_CULL_FACE); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_EQUAL, 0.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { _baseHeightfieldProgram.bind(); glEnableClientState(GL_TEXTURE_COORD_ARRAY); BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()); data.guide(heightfieldRenderVisitor); glDisableClientState(GL_TEXTURE_COORD_ARRAY); _baseHeightfieldProgram.release(); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderDualContourSurfaces)) { glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); _baseVoxelProgram.bind(); BufferRenderVisitor voxelRenderVisitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute()); data.guide(voxelRenderVisitor); _baseVoxelProgram.release(); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); } glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false); } void DefaultMetavoxelRendererImplementation::loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations) { program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_" + type + "_splat.vert"); program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/metavoxel_" + type + "_splat.frag"); program.link(); program.bind(); program.setUniformValue("heightMap", 0); program.setUniformValue("textureMap", 1); program.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT); locations.heightScale = program.uniformLocation("heightScale"); locations.textureScale = program.uniformLocation("textureScale"); locations.splatTextureOffset = program.uniformLocation("splatTextureOffset"); locations.splatTextureScalesS = program.uniformLocation("splatTextureScalesS"); locations.splatTextureScalesT = program.uniformLocation("splatTextureScalesT"); locations.textureValueMinima = program.uniformLocation("textureValueMinima"); locations.textureValueMaxima = program.uniformLocation("textureValueMaxima"); locations.materials = program.attributeLocation("materials"); locations.materialWeights = program.attributeLocation("materialWeights"); program.release(); } ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram; int DefaultMetavoxelRendererImplementation::_pointScaleLocation; ProgramObject DefaultMetavoxelRendererImplementation::_baseHeightfieldProgram; int DefaultMetavoxelRendererImplementation::_baseHeightScaleLocation;