From d1a046e6ed43be091aa819b6a88bbc7b2c129c1e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 29 Aug 2014 21:18:31 -0700 Subject: [PATCH] properly handle LOD in rendering by not rendering small entities --- interface/src/entities/EntityTreeRenderer.cpp | 169 +++++++++++------- interface/src/entities/EntityTreeRenderer.h | 4 + libraries/entities/src/todo.txt | 2 + libraries/shared/src/AACube.h | 1 + 4 files changed, 110 insertions(+), 66 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index d384f03727..955b2a110c 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -110,6 +110,101 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI return result; } +void renderElementProxy(EntityTreeElement* entityTreeElement) { + glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE; + float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE; + glColor3f(1.0f, 0.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z); + glutWireCube(elementSize); + glPopMatrix(); + + bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies); + + if (displayElementChildProxies) { + // draw the children + float halfSize = elementSize / 2.0f; + float quarterSize = elementSize / 4.0f; + glColor3f(1.0f, 1.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(1.0f, 0.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 1.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(1.0f, 1.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.5f, 0.5f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.5f, 0.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.5f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + } +} + +float EntityTreeRenderer::distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const { + glm::vec3 temp = viewFrustum.getPosition() - center; + float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp)); + return distanceToVoxelCenter; +} + +// TODO: This could be optimized to be a table, or something that doesn't require recalculation on every +// render call for every entity +// TODO: This is essentially the same logic used to render voxels, but since models are more detailed then voxels +// I've added a voxelToModelRatio that adjusts how much closer to a model you have to be to see it. +bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distanceToCamera) const { + const float voxelToModelRatio = 4.0f; // must be this many times closer to a model than a voxel to see it. + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + + float scale = (float)TREE_SCALE; + float visibleDistanceAtScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, voxelSizeScale) / voxelToModelRatio; + + while (scale > largestDimension) { + scale /= 2.0f; + visibleDistanceAtScale /= 2.0f; + } + + if (scale < largestDimension) { + visibleDistanceAtScale *= 2.0f; + } + + return (distanceToCamera <= visibleDistanceAtScale); +} + void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { bool wantDebug = false; @@ -132,73 +227,13 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE; bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy); - bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies); + if (!isShadowMode && displayElementProxy && numberOfEntities > 0) { - glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE; - float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE; - glColor3f(1.0f, 0.0f, 0.0f); - glPushMatrix(); - glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z); - glutWireCube(elementSize); - glPopMatrix(); - - if (displayElementChildProxies) { - // draw the children - float halfSize = elementSize / 2.0f; - float quarterSize = elementSize / 4.0f; - glColor3f(1.0f, 1.0f, 0.0f); - glPushMatrix(); - glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(1.0f, 0.0f, 1.0f); - glPushMatrix(); - glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(0.0f, 1.0f, 0.0f); - glPushMatrix(); - glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(0.0f, 0.0f, 1.0f); - glPushMatrix(); - glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(1.0f, 1.0f, 1.0f); - glPushMatrix(); - glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(0.0f, 0.5f, 0.5f); - glPushMatrix(); - glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(0.5f, 0.0f, 0.0f); - glPushMatrix(); - glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - - glColor3f(0.0f, 0.5f, 0.0f); - glPushMatrix(); - glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); - glutWireCube(halfSize); - glPopMatrix(); - } - + renderElementProxy(entityTreeElement); } - + for (uint16_t i = 0; i < numberOfEntities; i++) { EntityItem* entityItem = entityItems[i]; if (wantDebug) { @@ -210,13 +245,16 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) << "isBestFit=" << isBestFit; } - // render entityItem aspoints + // render entityItem AACube entityCube = entityItem->getAACube(); entityCube.scale(TREE_SCALE); // TODO: some entity types (like lights) might want to be rendered even // when they are outside of the view frustum... - if (args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) { + float distance = distanceToCamera(entityCube.calcCenter(), *args->_viewFrustum); + if (shouldRenderEntity(entityCube.getLargestDimension(), distance) && + args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) { + Glower* glower = NULL; if (entityItem->getGlowLevel() > 0.0f) { glower = new Glower(entityItem->getGlowLevel()); @@ -231,7 +269,6 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) if (glower) { delete glower; } - } else { args->_itemsOutOfView++; } diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 817212fa67..0c85ecdc18 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -76,6 +76,10 @@ public: void deleteReleasedModels(); private: QList _releasedModels; + + float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const; + bool shouldRenderEntity(float largestDimension, float distanceToCamera) const; + }; #endif // hifi_EntityTreeRenderer_h diff --git a/libraries/entities/src/todo.txt b/libraries/entities/src/todo.txt index 66ceec98d1..20dd7ba70b 100644 --- a/libraries/entities/src/todo.txt +++ b/libraries/entities/src/todo.txt @@ -4,6 +4,7 @@ doesn't include the extra exists bits will break something. 3) Make sure LOD logic honors the LOD settings for entities in "spanners/parent" cells. + -- network - don't SEND small entities even if their spanner cell is visible 7) some jutter with moving entities -- I think this might only happen with lots of models in an element or in view @@ -229,6 +230,7 @@ // FIXED -- animations don't appear on all viewers // FIXED -- animations are on different frames -- the data suggest they are the same frame number from a simulation perspective // SOLVED -- 50) Verify pruning logic... +// SOLVED -- 51) LOD for rendering - don't render small entities even if their spanner cell is visible diff --git a/libraries/shared/src/AACube.h b/libraries/shared/src/AACube.h index d01888429f..705c025d74 100644 --- a/libraries/shared/src/AACube.h +++ b/libraries/shared/src/AACube.h @@ -37,6 +37,7 @@ public: const glm::vec3& getCorner() const { return _corner; } float getScale() const { return _scale; } glm::vec3 getDimensions() const { return glm::vec3(_scale,_scale,_scale); } + float getLargestDimension() const { return _scale; } glm::vec3 calcCenter() const; glm::vec3 calcTopFarLeft() const;