From 10cdea9cd8356b82d051b43f4d1f3179399ea899 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jan 2017 14:17:50 -0800 Subject: [PATCH] cleanup model fade logic --- interface/src/avatar/CauterizedModel.cpp | 3 - .../render-utils/src/MeshPartPayload.cpp | 80 +++++++++---------- libraries/render-utils/src/MeshPartPayload.h | 15 ++-- libraries/render-utils/src/Model.cpp | 4 - libraries/shared/src/Interpolate.cpp | 9 ++- libraries/shared/src/Interpolate.h | 4 + 6 files changed, 58 insertions(+), 57 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index 30566e357d..02107e9d24 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -225,9 +225,6 @@ void CauterizedModel::updateRenderItems() { foreach (auto itemID, keys) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->getGeometryCounter()) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 57346ceb53..fa180a654a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -402,7 +402,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (!_hasFinishedFade) { + if (_fadeState != FADE_COMPLETE) { builder.withTransparent(); } @@ -472,7 +472,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || !_hasFinishedFade) { + if (isTranslucent || _fadeState != FADE_COMPLETE) { builder.withTranslucent(); } if (hasTangents) { @@ -513,9 +513,10 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - if (!_hasColorAttrib || fadeRatio < 1.0f) { - batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); + if (_fadeState != FADE_COMPLETE) { + batch._glColor4f(1.0f, 1.0f, 1.0f, computeFadeAlpha()); + } else if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } } @@ -528,17 +529,23 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -void ModelMeshPartPayload::startFade() { - bool shouldFade = EntityItem::getEntitiesShouldFadeFunction()(); - if (shouldFade) { - _fadeStartTime = usecTimestampNow(); - _hasStartedFade = true; - _hasFinishedFade = false; - } else { - _isFading = true; - _hasStartedFade = true; - _hasFinishedFade = true; +float ModelMeshPartPayload::computeFadeAlpha() const { + if (_fadeState == FADE_WAITING_TO_START) { + return 0.0f; } + float fadeAlpha = 1.0f; + const float INV_FADE_PERIOD = 1.0f / (float)(1 * USECS_PER_SECOND); + float fraction = (float)(usecTimestampNow() - _fadeStartTime) * INV_FADE_PERIOD; + if (fraction < 1.0f) { + fadeAlpha = Interpolate::simpleNonLinearBlend(fraction); + } + if (fadeAlpha >= 1.0f) { + _fadeState = FADE_COMPLETE; + // when fade-in completes we flag model for one last "render item update" + _model->setRenderItemsNeedUpdate(); + return 1.0f; + } + return Interpolate::simpleNonLinearBlend(fadeAlpha); } void ModelMeshPartPayload::render(RenderArgs* args) const { @@ -548,33 +555,28 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { return; // bail asap } - // If we didn't start the fade in, check if we are ready to now.... - if (!_hasStartedFade && _model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { - const_cast(*this).startFade(); + if (_fadeState == FADE_WAITING_TO_START) { + if (_model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) { + if (EntityItem::getEntitiesShouldFadeFunction()()) { + _fadeStartTime = usecTimestampNow(); + _fadeState = FADE_IN_PROGRESS; + } else { + _fadeState = FADE_COMPLETE; + } + _model->setRenderItemsNeedUpdate(); + } else { + return; + } } - // If we still didn't start the fade in, bail - if (!_hasStartedFade) { + if (!args) { return; } - - // When an individual mesh parts like this finishes its fade, we will mark the Model as - // having render items that need updating - bool nextIsFading = _isFading ? isStillFading() : false; - bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade; - bool endFading = _isFading && !nextIsFading; - if (startFading || endFading) { - _isFading = startFading; - _hasFinishedFade = endFading; - _model->setRenderItemsNeedUpdate(); - } - - gpu::Batch& batch = *(args->_batch); - if (!getShapeKey().isValid()) { return; } + gpu::Batch& batch = *(args->_batch); auto locations = args->_pipeline->locations; assert(locations); @@ -588,9 +590,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations); - if (args) { - args->_details._materialSwitches++; - } + args->_details._materialSwitches++; // Draw! { @@ -598,8 +598,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { drawCall(batch); } - if (args) { - const int INDICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; - } + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index a2c932b589..7d0aeab2bd 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -21,6 +21,10 @@ #include +const uint8_t FADE_WAITING_TO_START = 0; +const uint8_t FADE_IN_PROGRESS = 1; +const uint8_t FADE_COMPLETE = 2; + class Model; class MeshPartPayload { @@ -87,10 +91,7 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); - // Entity fade in - void startFade(); - bool hasStartedFade() { return _hasStartedFade; } - bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + float computeFadeAlpha() const; // Render Item interface render::ItemKey getKey() const override; @@ -113,10 +114,8 @@ public: bool _isBlendShaped{ false }; private: - quint64 _fadeStartTime { 0 }; - bool _hasStartedFade { false }; - mutable bool _hasFinishedFade { false }; - mutable bool _isFading { false }; + mutable quint64 _fadeStartTime { 0 }; + mutable uint8_t _fadeState { FADE_WAITING_TO_START }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ef613e8c14..b79e69a9b7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -227,9 +227,6 @@ void Model::updateRenderItems() { foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { - if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { - data.startFade(); - } // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. @@ -1450,7 +1447,6 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { { Lock lock(_mutex); - _modelsRequiringBlends.insert(model); } } diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index b20c7b8fac..ba93a21a8e 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -61,6 +61,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) { } } +float Interpolate::simpleNonLinearBlend(float fraction) { + // uses arctan() to map a linear distribution in domain [0,1] to a non-linear blend (slow out, slow in) in range [0,1] + const float WIDTH = 20.0f; + const float INV_ARCTAN_WIDTH = 0.339875327433f; // 1 / (2 * atan(WIDTH/2)) + return 0.5f + atanf(WIDTH * (fraction - 0.5f)) * INV_ARCTAN_WIDTH; +} + float Interpolate::calculateFadeRatio(quint64 start) { const float FADE_TIME = 1.0f; float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); @@ -69,4 +76,4 @@ float Interpolate::calculateFadeRatio(quint64 start) { // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly const float EASING_SCALE = 1.001f; return std::min(EASING_SCALE * fadeRatio, 1.0f); -} \ No newline at end of file +} diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h index a9fa5baad2..79ebd2f7fc 100644 --- a/libraries/shared/src/Interpolate.h +++ b/libraries/shared/src/Interpolate.h @@ -25,6 +25,10 @@ public: // pass through all three y values. Return value lies wholly within the range of y values passed in. static float interpolate3Points(float y1, float y2, float y3, float u); + // returns smooth in and out blend between 0 and 1 + // DANGER: assumes fraction is properly inside range [0, 1] + static float simpleNonLinearBlend(float fraction); + static float calculateFadeRatio(quint64 start); };