// There are two types of nodes for which we want to "render" // 1) Leaves that are in the LOD // 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children // wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE // in the LOD, and others that are not. In this case we want to render the parent, and none of the children. // // Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" // corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of. // By doing this, we don't need to test each child voxel's position vs the LOD boundary bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const { bool shouldRender = false; if (hasContent()) { float furthestDistance = furthestDistanceToCamera(*viewFrustum); float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); bool inChildBoundary = (furthestDistance <= childBoundary); if (hasDetailedContent() && inChildBoundary) { shouldRender = true; } else { float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary bool inBoundary = (furthestDistance <= boundary); shouldRender = inBoundary && !inChildBoundary; } } return shouldRender; }
float calculateRenderAccuracy(const glm::vec3& position, const AABox& bounds, float octreeSizeScale, int boundaryLevelAdjust) { float largestDimension = bounds.getLargestDimension(); const float maxScale = (float)TREE_SCALE; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; static std::once_flag once; static QMap<float, float> shouldRenderTable; std::call_once(once, [&] { float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small float scale = maxScale; float factor = 1.0f; while (scale > SMALLEST_SCALE_IN_TABLE) { scale /= 2.0f; factor /= 2.0f; shouldRenderTable[scale] = factor; } }); float closestScale = maxScale; float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; QMap<float, float>::const_iterator lowerBound = shouldRenderTable.lowerBound(largestDimension); if (lowerBound != shouldRenderTable.constEnd()) { closestScale = lowerBound.key(); visibleDistanceAtClosestScale = visibleDistanceAtMaxScale * lowerBound.value(); } if (closestScale < largestDimension) { visibleDistanceAtClosestScale *= 2.0f; } // FIXME - for now, it's either visible or not visible. We want to adjust this to eventually return // a floating point for objects that have small angular size to indicate that they may be rendered // with lower preciscion float distanceToCamera = glm::length(bounds.calcCenter() - position); return (distanceToCamera <= visibleDistanceAtClosestScale) ? 1.0f : 0.0f; }
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { const float maxScale = (float)TREE_SCALE; const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it. float octreeSizeScale = args->_sizeScale; int boundaryLevelAdjust = args->_boundaryLevelAdjust; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio; float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition()); float largestDimension = bounds.getLargestDimension(); static bool shouldRenderTableNeedsBuilding = true; static QMap<float, float> shouldRenderTable; if (shouldRenderTableNeedsBuilding) { float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small float scale = maxScale; float factor = 1.0f; while (scale > SMALLEST_SCALE_IN_TABLE) { scale /= 2.0f; factor /= 2.0f; shouldRenderTable[scale] = factor; } shouldRenderTableNeedsBuilding = false; } float closestScale = maxScale; float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; QMap<float, float>::const_iterator lowerBound = shouldRenderTable.lowerBound(largestDimension); if (lowerBound != shouldRenderTable.constEnd()) { closestScale = lowerBound.key(); visibleDistanceAtClosestScale = visibleDistanceAtMaxScale * lowerBound.value(); } if (closestScale < largestDimension) { visibleDistanceAtClosestScale *= 2.0f; } return distanceToCamera <= visibleDistanceAtClosestScale; };
// TODO: This is essentially the same logic used to render octree cells, but since models are more detailed then octree cells // I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it. bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) { const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it. float octreeSizeScale = getOctreeSizeScale(); int boundaryLevelAdjust = getBoundaryLevelAdjust(); float maxScale = (float)TREE_SCALE; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio; if (_shouldRenderTableNeedsRebuilding) { _shouldRenderTable.clear(); float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small float scale = maxScale; float visibleDistanceAtScale = visibleDistanceAtMaxScale; while (scale > SMALLEST_SCALE_IN_TABLE) { scale /= 2.0f; visibleDistanceAtScale /= 2.0f; _shouldRenderTable[scale] = visibleDistanceAtScale; } _shouldRenderTableNeedsRebuilding = false; } float closestScale = maxScale; float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; QMap<float, float>::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension); if (lowerBound != _shouldRenderTable.constEnd()) { closestScale = lowerBound.key(); visibleDistanceAtClosestScale = lowerBound.value(); } if (closestScale < largestDimension) { visibleDistanceAtClosestScale *= 2.0f; } return (distanceToCamera <= visibleDistanceAtClosestScale); }
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) { const float maxScale = (float)TREE_SCALE; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO; return atan(maxScale / visibleDistanceAtMaxScale); }