diff --git a/libraries/render-utils/src/FadeEffect.cpp b/libraries/render-utils/src/FadeEffect.cpp index b2fd7c0c7e..aec19ce32f 100644 --- a/libraries/render-utils/src/FadeEffect.cpp +++ b/libraries/render-utils/src/FadeEffect.cpp @@ -1,730 +1,46 @@ +// +// FadeEffect.cpp + +// Created by Olivier Prat on 17/07/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// #include "FadeEffect.h" +#include "FadeEffectJobs.h" #include "TextureCache.h" -#include "render/Logging.h" + #include "render/TransitionStage.h" #include -#include -#include -#include -#include - -#define FADE_MIN_SCALE 0.001 -#define FADE_MAX_SCALE 10000.0 -#define FADE_MAX_SPEED 50.f - -inline float parameterToValuePow(float parameter, const double minValue, const double maxOverMinValue) { - return (float)(minValue * pow(maxOverMinValue, double(parameter))); +FadeEffect::FadeEffect() { + auto texturePath = PathUtils::resourcesPath() + "images/fadeMask.png"; + _maskMap = DependencyManager::get()->getImageTexture(texturePath, image::TextureUsage::STRICT_TEXTURE); } -inline float valueToParameterPow(float value, const double minValue, const double maxOverMinValue) { - return (float)(log(double(value) / minValue) / log(maxOverMinValue)); +void FadeEffect::build(render::Task::TaskConcept& task, const task::Varying& editableItems) { + auto editedFadeCategory = task.addJob("Fade"); + auto& fadeJob = task._jobs.back(); + _configurations = fadeJob.get().getConfigurationBuffer(); + + const auto fadeEditInput = FadeEditJob::Input(editableItems, editedFadeCategory).asVarying(); + task.addJob("FadeEdit", fadeEditInput); } -void FadeEditJob::configure(const Config& config) { - _isEditEnabled = config.editFade; -} - -void FadeEditJob::run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs) { - auto scene = renderContext->_scene; - - if (_isEditEnabled) { - float minIsectDistance = std::numeric_limits::max(); - auto& itemBounds = inputs.get0(); - auto editedItem = findNearestItem(renderContext, itemBounds, minIsectDistance); - render::Transaction transaction; - bool hasTransaction{ false }; - - if (editedItem != _editedItem && render::Item::isValidID(_editedItem)) { - // Remove transition from previously edited item as we've changed edited item - hasTransaction = true; - transaction.removeTransitionFromItem(_editedItem); - } - _editedItem = editedItem; - - if (render::Item::isValidID(_editedItem)) { - static const render::Transition::Type categoryToTransition[FadeConfig::CATEGORY_COUNT] = { - render::Transition::ELEMENT_ENTER_DOMAIN, - render::Transition::BUBBLE_ISECT_OWNER, - render::Transition::BUBBLE_ISECT_TRESPASSER, - render::Transition::USER_ENTER_DOMAIN, - render::Transition::AVATAR_CHANGE - }; - - auto transitionType = categoryToTransition[inputs.get1()]; - - transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) { - if (transition == nullptr || transition->isFinished || transition->eventType!=transitionType) { - // Relaunch transition - render::Transaction transaction; - transaction.addTransitionToItem(id, transitionType); - scene->enqueueTransaction(transaction); - } - }); - hasTransaction = true; - } - - if (hasTransaction) { - scene->enqueueTransaction(transaction); - } - } - else if (render::Item::isValidID(_editedItem)) { - // Remove transition from previously edited item as we've disabled fade edition - render::Transaction transaction; - transaction.removeTransitionFromItem(_editedItem); - scene->enqueueTransaction(transaction); - _editedItem = render::Item::INVALID_ITEM_ID; - } -} - -render::ItemID FadeEditJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { - const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); - const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); - BoxFace face; - glm::vec3 normal; - float isectDistance; - render::ItemID nearestItem = render::Item::INVALID_ITEM_ID; - const float minDistance = 2.f; - - for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { - if (isectDistance>minDistance && isectDistance < minIsectDistance) { - nearestItem = itemBound.id; - minIsectDistance = isectDistance; - } - } - } - return nearestItem; -} - -FadeConfig::FadeConfig() -{ - events[ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f }; - events[ELEMENT_ENTER_LEAVE_DOMAIN].noiseLevel = 1.f; - events[ELEMENT_ENTER_LEAVE_DOMAIN].noiseSpeed = glm::vec3{ 0.0f, 0.0f, 0.0f }; - events[ELEMENT_ENTER_LEAVE_DOMAIN].timing = FadeConfig::LINEAR; - events[ELEMENT_ENTER_LEAVE_DOMAIN].baseSize = glm::vec3{ 1.0f, 1.0f, 1.0f }; - events[ELEMENT_ENTER_LEAVE_DOMAIN].baseLevel = 0.f; - events[ELEMENT_ENTER_LEAVE_DOMAIN].isInverted = false; - events[ELEMENT_ENTER_LEAVE_DOMAIN].duration = 4.f; - events[ELEMENT_ENTER_LEAVE_DOMAIN].edgeWidth = 0.1f; - events[ELEMENT_ENTER_LEAVE_DOMAIN].edgeInnerColor = glm::vec4{ 78.f / 255.f, 215.f / 255.f, 255.f / 255.f, 0.0f }; - events[ELEMENT_ENTER_LEAVE_DOMAIN].edgeOuterColor = glm::vec4{ 78.f / 255.f, 215.f / 255.f, 255.f / 255.f, 1.0f }; - - events[BUBBLE_ISECT_OWNER].noiseSize = glm::vec3{ 1.5f, 1.0f/25.f, 0.5f }; - events[BUBBLE_ISECT_OWNER].noiseLevel = 0.37f; - events[BUBBLE_ISECT_OWNER].noiseSpeed = glm::vec3{ 1.0f, 0.2f, 1.0f }; - events[BUBBLE_ISECT_OWNER].timing = FadeConfig::LINEAR; - events[BUBBLE_ISECT_OWNER].baseSize = glm::vec3{ 2.0f, 2.0f, 2.0f }; - events[BUBBLE_ISECT_OWNER].baseLevel = 1.f; - events[BUBBLE_ISECT_OWNER].isInverted = false; - events[BUBBLE_ISECT_OWNER].duration = 4.f; - events[BUBBLE_ISECT_OWNER].edgeWidth = 0.02f; - events[BUBBLE_ISECT_OWNER].edgeInnerColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 1.0f }; - events[BUBBLE_ISECT_OWNER].edgeOuterColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 2.0f }; - - events[BUBBLE_ISECT_TRESPASSER].noiseSize = glm::vec3{ 0.5f, 1.0f / 25.f, 0.5f }; - events[BUBBLE_ISECT_TRESPASSER].noiseLevel = 1.f; - events[BUBBLE_ISECT_TRESPASSER].noiseSpeed = glm::vec3{ 1.0f, -5.f, 1.0f }; - events[BUBBLE_ISECT_TRESPASSER].timing = FadeConfig::LINEAR; - events[BUBBLE_ISECT_TRESPASSER].baseSize = glm::vec3{ 2.0f, 2.0f, 2.0f }; - events[BUBBLE_ISECT_TRESPASSER].baseLevel = 0.f; - events[BUBBLE_ISECT_TRESPASSER].isInverted = false; - events[BUBBLE_ISECT_TRESPASSER].duration = 4.f; - events[BUBBLE_ISECT_TRESPASSER].edgeWidth = 0.025f; - events[BUBBLE_ISECT_TRESPASSER].edgeInnerColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 1.0f }; - events[BUBBLE_ISECT_TRESPASSER].edgeOuterColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 2.0f }; - - events[USER_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 10.f, 0.01f, 10.0f }; - events[USER_ENTER_LEAVE_DOMAIN].noiseLevel = 0.3f; - events[USER_ENTER_LEAVE_DOMAIN].noiseSpeed = glm::vec3{ 0.0f, -0.5f, 0.0f }; - events[USER_ENTER_LEAVE_DOMAIN].timing = FadeConfig::LINEAR; - events[USER_ENTER_LEAVE_DOMAIN].baseSize = glm::vec3{ 10000.f, 1.0f, 10000.0f }; - events[USER_ENTER_LEAVE_DOMAIN].baseLevel = 1.f; - events[USER_ENTER_LEAVE_DOMAIN].isInverted = true; - events[USER_ENTER_LEAVE_DOMAIN].duration = 2.f; - events[USER_ENTER_LEAVE_DOMAIN].edgeWidth = 0.229f; - events[USER_ENTER_LEAVE_DOMAIN].edgeInnerColor = glm::vec4{ 1.f, 0.63f, 0.13f, 0.5f }; - events[USER_ENTER_LEAVE_DOMAIN].edgeOuterColor = glm::vec4{ 1.f, 1.f, 1.f, 1.0f }; - - events[AVATAR_CHANGE].noiseSize = glm::vec3{ 0.4f, 0.4f, 0.4f }; - events[AVATAR_CHANGE].noiseLevel = 1.f; - events[AVATAR_CHANGE].noiseSpeed = glm::vec3{ 0.0f, 0.0f, 0.0f }; - events[AVATAR_CHANGE].timing = FadeConfig::LINEAR; - events[AVATAR_CHANGE].baseSize = glm::vec3{ 0.4f, 0.4f, 0.4f }; - events[AVATAR_CHANGE].baseLevel = 1.f; - events[AVATAR_CHANGE].isInverted = false; - events[AVATAR_CHANGE].duration = 3.f; - events[AVATAR_CHANGE].edgeWidth = 0.05f; - events[AVATAR_CHANGE].edgeInnerColor = glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f }; - events[AVATAR_CHANGE].edgeOuterColor = glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f }; -} - -void FadeConfig::setEditedCategory(int value) { - assert(value < CATEGORY_COUNT); - editedCategory = std::min(CATEGORY_COUNT, value); - emit dirtyCategory(); - emit dirty(); -} - -void FadeConfig::setDuration(float value) { - events[editedCategory].duration = value; - emit dirty(); -} - -float FadeConfig::getDuration() const { - return events[editedCategory].duration; -} - -void FadeConfig::setBaseSizeX(float value) { - events[editedCategory].baseSize.x = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getBaseSizeX() const { - return valueToParameterPow(events[editedCategory].baseSize.x, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setBaseSizeY(float value) { - events[editedCategory].baseSize.y = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getBaseSizeY() const { - return valueToParameterPow(events[editedCategory].baseSize.y, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setBaseSizeZ(float value) { - events[editedCategory].baseSize.z = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getBaseSizeZ() const { - return valueToParameterPow(events[editedCategory].baseSize.z, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setBaseLevel(float value) { - events[editedCategory].baseLevel = value; - emit dirty(); -} - -void FadeConfig::setInverted(bool value) { - events[editedCategory].isInverted = value; - emit dirty(); -} - -bool FadeConfig::isInverted() const { - return events[editedCategory].isInverted; -} - -void FadeConfig::setNoiseSizeX(float value) { - events[editedCategory].noiseSize.x = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getNoiseSizeX() const { - return valueToParameterPow(events[editedCategory].noiseSize.x, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setNoiseSizeY(float value) { - events[editedCategory].noiseSize.y = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getNoiseSizeY() const { - return valueToParameterPow(events[editedCategory].noiseSize.y, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setNoiseSizeZ(float value) { - events[editedCategory].noiseSize.z = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); - emit dirty(); -} - -float FadeConfig::getNoiseSizeZ() const { - return valueToParameterPow(events[editedCategory].noiseSize.z, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); -} - -void FadeConfig::setNoiseLevel(float value) { - events[editedCategory].noiseLevel = value; - emit dirty(); -} - -void FadeConfig::setNoiseSpeedX(float value) { - events[editedCategory].noiseSpeed.x = powf(value, 3.f)*FADE_MAX_SPEED; - emit dirty(); -} - -float FadeConfig::getNoiseSpeedX() const { - return powf(events[editedCategory].noiseSpeed.x / FADE_MAX_SPEED, 1.f / 3.f); -} - -void FadeConfig::setNoiseSpeedY(float value) { - events[editedCategory].noiseSpeed.y = powf(value, 3.f)*FADE_MAX_SPEED; - emit dirty(); -} - -float FadeConfig::getNoiseSpeedY() const { - return powf(events[editedCategory].noiseSpeed.y / FADE_MAX_SPEED, 1.f / 3.f); -} - -void FadeConfig::setNoiseSpeedZ(float value) { - events[editedCategory].noiseSpeed.z = powf(value, 3.f)*FADE_MAX_SPEED; - emit dirty(); -} - -float FadeConfig::getNoiseSpeedZ() const { - return powf(events[editedCategory].noiseSpeed.z / FADE_MAX_SPEED, 1.f / 3.f); -} - -void FadeConfig::setEdgeWidth(float value) { - events[editedCategory].edgeWidth = value * value; - emit dirty(); -} - -float FadeConfig::getEdgeWidth() const { - return sqrtf(events[editedCategory].edgeWidth); -} - -void FadeConfig::setEdgeInnerColorR(float value) { - events[editedCategory].edgeInnerColor.r = value; - emit dirty(); -} - -void FadeConfig::setEdgeInnerColorG(float value) { - events[editedCategory].edgeInnerColor.g = value; - emit dirty(); -} - -void FadeConfig::setEdgeInnerColorB(float value) { - events[editedCategory].edgeInnerColor.b = value; - emit dirty(); -} - -void FadeConfig::setEdgeInnerIntensity(float value) { - events[editedCategory].edgeInnerColor.a = value; - emit dirty(); -} - -void FadeConfig::setEdgeOuterColorR(float value) { - events[editedCategory].edgeOuterColor.r = value; - emit dirty(); -} - -void FadeConfig::setEdgeOuterColorG(float value) { - events[editedCategory].edgeOuterColor.g = value; - emit dirty(); -} - -void FadeConfig::setEdgeOuterColorB(float value) { - events[editedCategory].edgeOuterColor.b = value; - emit dirty(); -} - -void FadeConfig::setEdgeOuterIntensity(float value) { - events[editedCategory].edgeOuterColor.a = value; - emit dirty(); -} - -void FadeConfig::setTiming(int value) { - assert(value < TIMING_COUNT); - events[editedCategory].timing = value; - emit dirty(); -} - -QString FadeConfig::eventNames[FadeConfig::CATEGORY_COUNT] = { - "element_enter_leave_domain", - "bubble_isect_owner", - "bubble_isect_trespasser", - "user_enter_leave_domain", - "avatar_change", -}; - -void FadeConfig::save() const { - assert(editedCategory < FadeConfig::CATEGORY_COUNT); - QJsonObject lProperties; - const QString configFile = "config/" + eventNames[editedCategory] + ".json"; - QUrl path(PathUtils::resourcesPath() + configFile); - QFile file(path.toString()); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - qWarning() << "Fade event configuration file " << path << " cannot be opened"; - } - else { - const auto& event = events[editedCategory]; - - lProperties["edgeInnerColor"] = QJsonArray{ event.edgeInnerColor.r, event.edgeInnerColor.g, event.edgeInnerColor.b, event.edgeInnerColor.a }; - lProperties["edgeOuterColor"] = QJsonArray{ event.edgeOuterColor.r, event.edgeOuterColor.g, event.edgeOuterColor.b, event.edgeOuterColor.a }; - lProperties["noiseSize"] = QJsonArray{ event.noiseSize.x, event.noiseSize.y, event.noiseSize.z }; - lProperties["noiseSpeed"] = QJsonArray{ event.noiseSpeed.x, event.noiseSpeed.y, event.noiseSpeed.z }; - lProperties["baseSize"] = QJsonArray{ event.baseSize.x, event.baseSize.y, event.baseSize.z }; - lProperties["noiseLevel"] = event.noiseLevel; - lProperties["baseLevel"] = event.baseLevel; - lProperties["duration"] = event.duration; - lProperties["edgeWidth"] = event.edgeWidth; - lProperties["timing"] = event.timing; - lProperties["isInverted"] = event.isInverted; - - file.write( QJsonDocument(lProperties).toJson() ); - file.close(); - } -} - -void FadeConfig::load() { - const QString configFile = "config/" + eventNames[editedCategory] + ".json"; - - QUrl path(PathUtils::resourcesPath() + configFile); - QFile file(path.toString()); - if (!file.exists()) { - qWarning() << "Fade event configuration file " << path << " does not exist"; - } - else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning() << "Fade event configuration file " << path << " cannot be opened"; - } - else { - QString fileData = file.readAll(); - file.close(); - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(fileData.toUtf8(), &error); - if (error.error == error.NoError) { - QJsonObject jsonObject = doc.object(); - QJsonValue value; - auto& event = events[editedCategory]; - - qCDebug(renderlogging) << "Fade event configuration file" << path << "loaded"; - - value = jsonObject["edgeInnerColor"]; - if (value.isArray()) { - QJsonArray data = value.toArray(); - - if (data.size() < 4) { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4"; - } - else { - event.edgeInnerColor.r = (float)data.at(0).toDouble(); - event.edgeInnerColor.g = (float)data.at(1).toDouble(); - event.edgeInnerColor.b = (float)data.at(2).toDouble(); - event.edgeInnerColor.a = (float)data.at(3).toDouble(); - } - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4"; - } - - value = jsonObject["edgeOuterColor"]; - if (value.isArray()) { - QJsonArray data = value.toArray(); - - if (data.size() < 4) { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4"; - } - else { - event.edgeOuterColor.r = (float)data.at(0).toDouble(); - event.edgeOuterColor.g = (float)data.at(1).toDouble(); - event.edgeOuterColor.b = (float)data.at(2).toDouble(); - event.edgeOuterColor.a = (float)data.at(3).toDouble(); - } - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4"; - } - - value = jsonObject["noiseSize"]; - if (value.isArray()) { - QJsonArray data = value.toArray(); - - if (data.size() < 3) { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3"; - } - else { - event.noiseSize.x = (float)data.at(0).toDouble(); - event.noiseSize.y = (float)data.at(1).toDouble(); - event.noiseSize.z = (float)data.at(2).toDouble(); - } - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3"; - } - - value = jsonObject["noiseSpeed"]; - if (value.isArray()) { - QJsonArray data = value.toArray(); - - if (data.size() < 3) { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3"; - } - else { - event.noiseSpeed.x = (float)data.at(0).toDouble(); - event.noiseSpeed.y = (float)data.at(1).toDouble(); - event.noiseSpeed.z = (float)data.at(2).toDouble(); - } - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3"; - } - - value = jsonObject["baseSize"]; - if (value.isArray()) { - QJsonArray data = value.toArray(); - - if (data.size() < 3) { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3"; - } - else { - event.baseSize.x = (float)data.at(0).toDouble(); - event.baseSize.y = (float)data.at(1).toDouble(); - event.baseSize.z = (float)data.at(2).toDouble(); - } - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3"; - } - - value = jsonObject["noiseLevel"]; - if (value.isDouble()) { - event.noiseLevel = (float)value.toDouble(); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseLevel' field. Expected float value"; - } - - value = jsonObject["baseLevel"]; - if (value.isDouble()) { - event.baseLevel = (float)value.toDouble(); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseLevel' field. Expected float value"; - } - - value = jsonObject["duration"]; - if (value.isDouble()) { - event.duration = (float)value.toDouble(); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'duration' field. Expected float value"; - } - - value = jsonObject["edgeWidth"]; - if (value.isDouble()) { - event.edgeWidth = std::min(1.f, std::max(0.f, (float)value.toDouble())); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeWidth' field. Expected float value"; - } - - value = jsonObject["timing"]; - if (value.isDouble()) { - event.timing = std::max(0, std::min(TIMING_COUNT - 1, value.toInt())); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'timing' field. Expected integer value"; - } - - value = jsonObject["isInverted"]; - if (value.isBool()) { - event.isInverted = value.toBool(); - } - else { - qWarning() << "Fade event configuration file " << path << " contains an invalid 'isInverted' field. Expected boolean value"; - } - - emit dirty(); - } - else { - qWarning() << "Fade event configuration file" << path << "failed to load:" << - error.errorString() << "at offset" << error.offset; - } - } -} - -FadeJob::FadeJob() -{ - auto texturePath = PathUtils::resourcesPath() + "images/fadeMask.png"; - _fadeMaskMap = DependencyManager::get()->getImageTexture(texturePath, image::TextureUsage::STRICT_TEXTURE); - _previousTime = usecTimestampNow(); -} - -void FadeJob::configure(const Config& config) { - auto& configurations = _configurations.edit(); - - for (auto i = 0; i < FadeConfig::CATEGORY_COUNT; i++) { - auto& eventParameters = configurations.parameters[i]; - const auto& eventConfig = config.events[i]; - - eventParameters._baseLevel = eventConfig.baseLevel; - eventParameters._noiseInvSizeAndLevel.x = 1.f / eventConfig.noiseSize.x; - eventParameters._noiseInvSizeAndLevel.y = 1.f / eventConfig.noiseSize.y; - eventParameters._noiseInvSizeAndLevel.z = 1.f / eventConfig.noiseSize.z; - eventParameters._noiseInvSizeAndLevel.w = eventConfig.noiseLevel; - eventParameters._isInverted = eventConfig.isInverted & 1; - eventParameters._edgeWidthInvWidth.x = eventConfig.edgeWidth; - eventParameters._edgeWidthInvWidth.y = 1.f / eventParameters._edgeWidthInvWidth.x; - eventParameters._innerEdgeColor = eventConfig.edgeInnerColor; - eventParameters._outerEdgeColor = eventConfig.edgeOuterColor; - _thresholdScale[i] = 1.f + (eventParameters._edgeWidthInvWidth.x + std::max(0.f, (eventConfig.noiseLevel + eventConfig.baseLevel)*0.5f - 0.5f)); - } -} - -void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Output& output) { - Config* jobConfig = static_cast(renderContext->jobConfig.get()); - auto scene = renderContext->args->_scene; - auto transitionStage = scene->getStage(render::TransitionStage::getName()); - uint64_t now = usecTimestampNow(); - const double deltaTime = (int64_t(now) - int64_t(_previousTime)) / double(USECS_PER_SECOND); - bool isFirstItem = true; - - output = (FadeConfig::Category) jobConfig->editedCategory; - - // And now update fade effect - for (auto transitionId : *transitionStage) { - auto& state = transitionStage->editTransition(transitionId); - update(*jobConfig, scene, state, deltaTime); - if (isFirstItem) { - jobConfig->setProperty("threshold", state.threshold); - isFirstItem = false; - } - } - _previousTime = now; -} - -const FadeConfig::Category FadeJob::transitionToCategory[render::Transition::TYPE_COUNT] = { - FadeConfig::ELEMENT_ENTER_LEAVE_DOMAIN, - FadeConfig::ELEMENT_ENTER_LEAVE_DOMAIN, - FadeConfig::BUBBLE_ISECT_OWNER, - FadeConfig::BUBBLE_ISECT_TRESPASSER, - FadeConfig::USER_ENTER_LEAVE_DOMAIN, - FadeConfig::USER_ENTER_LEAVE_DOMAIN, - FadeConfig::AVATAR_CHANGE -}; - -void FadeJob::update(const Config& config, const render::ScenePointer& scene, render::Transition& transition, const double deltaTime) const { - const auto fadeCategory = transitionToCategory[transition.eventType]; - auto& eventConfig = config.events[fadeCategory]; - auto& item = scene->getItem(transition.itemId); - const double eventDuration = (double)eventConfig.duration; - const FadeConfig::Timing timing = (FadeConfig::Timing) eventConfig.timing; - - if (item.exist()) { - auto aabb = item.getBound(); - if (render::Item::isValidID(transition.boundItemId)) { - auto& boundItem = scene->getItem(transition.boundItemId); - if (boundItem.exist()) { - aabb = boundItem.getBound(); - } - } - auto& dimensions = aabb.getDimensions(); - - assert(timing < FadeConfig::TIMING_COUNT); - - transition.noiseOffset = aabb.calcCenter(); - transition.baseInvSize.x = 1.f / eventConfig.baseSize.x; - transition.baseInvSize.y = 1.f / eventConfig.baseSize.y; - transition.baseInvSize.z = 1.f / eventConfig.baseSize.z; - - switch (transition.eventType) { - case render::Transition::ELEMENT_ENTER_DOMAIN: - case render::Transition::ELEMENT_LEAVE_DOMAIN: - { - transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing); - transition.baseOffset = transition.noiseOffset; - transition.baseInvSize.x = 1.f / dimensions.x; - transition.baseInvSize.y = 1.f / dimensions.y; - transition.baseInvSize.z = 1.f / dimensions.z; - transition.isFinished += (transition.threshold >= 1.f) & 1; - if (transition.eventType == render::Transition::ELEMENT_ENTER_DOMAIN) { - transition.threshold = 1.f - transition.threshold; - } - } - break; - - case render::Transition::BUBBLE_ISECT_OWNER: - { - transition.threshold = 0.5f; - transition.baseOffset = transition.noiseOffset; - } - break; - - case render::Transition::BUBBLE_ISECT_TRESPASSER: - { - transition.threshold = 0.5f; - transition.baseOffset = transition.noiseOffset; - } - break; - - case render::Transition::USER_ENTER_DOMAIN: - case render::Transition::USER_LEAVE_DOMAIN: - { - transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing); - transition.baseOffset = transition.noiseOffset - dimensions.y / 2.f; - transition.baseInvSize.y = 1.f / dimensions.y; - transition.isFinished += (transition.threshold >= 1.f) & 1; - if (transition.eventType == render::Transition::USER_LEAVE_DOMAIN) { - transition.threshold = 1.f - transition.threshold; - } - } - break; - - case render::Transition::AVATAR_CHANGE: - break; - - default: - assert(false); - } - } - - transition.noiseOffset += eventConfig.noiseSpeed * (float)transition.time; - if (config.manualFade) { - transition.threshold = config.manualThreshold; - } - transition.threshold = std::max(0.f, std::min(1.f, transition.threshold)); - transition.threshold = (transition.threshold - 0.5f)*_thresholdScale[fadeCategory] + 0.5f; - transition.time += deltaTime; - - // If the transition is finished for more than a number of frames (here 3), garbage collect it. - if (transition.isFinished > 3) { - render::Transaction transaction; - transaction.removeTransitionFromItem(transition.itemId); - scene->enqueueTransaction(transaction); - } -} - -float FadeJob::computeElementEnterRatio(double time, const double period, FadeConfig::Timing timing) { - assert(period > 0.0); - float fadeAlpha = 1.0f; - const double INV_FADE_PERIOD = 1.0 / period; - double fraction = time * INV_FADE_PERIOD; - fraction = std::max(fraction, 0.0); - if (fraction < 1.0) { - switch (timing) { - default: - fadeAlpha = (float)fraction; - break; - case FadeConfig::EASE_IN: - fadeAlpha = (float)(fraction*fraction*fraction); - break; - case FadeConfig::EASE_OUT: - fadeAlpha = 1.f - (float)fraction; - fadeAlpha = 1.f- fadeAlpha*fadeAlpha*fadeAlpha; - break; - case FadeConfig::EASE_IN_OUT: - fadeAlpha = (float)(fraction*fraction*fraction*(fraction*(fraction * 6 - 15) + 10)); - break; - } - } - return fadeAlpha; -} - -render::ShapePipeline::BatchSetter FadeJob::getBatchSetter() const { +render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() const { return [this](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args*) { auto program = shapePipeline.pipeline->getProgram(); auto maskMapLocation = program->getTextures().findLocation("fadeMaskMap"); auto bufferLocation = program->getUniformBuffers().findLocation("fadeParametersBuffer"); - batch.setResourceTexture(maskMapLocation, _fadeMaskMap); + batch.setResourceTexture(maskMapLocation, _maskMap); batch.setUniformBuffer(bufferLocation, _configurations); }; } -render::ShapePipeline::ItemSetter FadeJob::getItemSetter() const { - return [this](const render::ShapePipeline& shapePipeline, render::Args* args, const render::Item& item) { +render::ShapePipeline::ItemSetter FadeEffect::getItemSetter() const { + return [](const render::ShapePipeline& shapePipeline, render::Args* args, const render::Item& item) { if (!render::TransitionStage::isIndexInvalid(item.getTransitionId())) { auto scene = args->_scene; auto batch = args->_batch; @@ -732,7 +48,6 @@ render::ShapePipeline::ItemSetter FadeJob::getItemSetter() const { auto& transitionState = transitionStage->getTransition(item.getTransitionId()); render::ShapeKey shapeKey(args->_globalShapeKey); - // TODO test various cases: polyvox... etc // This is the normal case where we need to push the parameters in uniforms { auto program = shapePipeline.pipeline->getProgram(); @@ -744,7 +59,7 @@ render::ShapePipeline::ItemSetter FadeJob::getItemSetter() const { auto fadeCategoryLocation = uniforms.findLocation("fadeCategory"); if (fadeNoiseOffsetLocation >= 0 || fadeBaseInvSizeLocation >= 0 || fadeBaseOffsetLocation >= 0 || fadeThresholdLocation >= 0 || fadeCategoryLocation >= 0) { - const auto fadeCategory = transitionToCategory[transitionState.eventType]; + const auto fadeCategory = FadeJob::transitionToCategory[transitionState.eventType]; batch->_glUniform1i(fadeCategoryLocation, fadeCategory); batch->_glUniform1f(fadeThresholdLocation, transitionState.threshold); diff --git a/libraries/render-utils/src/FadeEffect.h b/libraries/render-utils/src/FadeEffect.h index 30c233ad29..8677609892 100644 --- a/libraries/render-utils/src/FadeEffect.h +++ b/libraries/render-utils/src/FadeEffect.h @@ -1,7 +1,7 @@ // // FadeEffect.h -// Created by Olivier Prat on 07/07/2017. +// Created by Olivier Prat on 17/07/2017. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -11,238 +11,26 @@ #ifndef hifi_render_utils_FadeEffect_h #define hifi_render_utils_FadeEffect_h -#include -#include -#include -#include +#include +#include -class FadeEditConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(bool editFade MEMBER editFade NOTIFY dirty) +class FadeEffect : public Dependency { + SINGLETON_DEPENDENCY; public: - bool editFade{ false }; - -signals: - - void dirty(); -}; - -class FadeConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(int editedCategory MEMBER editedCategory WRITE setEditedCategory NOTIFY dirtyCategory) - Q_PROPERTY(float duration READ getDuration WRITE setDuration NOTIFY dirty) - Q_PROPERTY(float baseSizeX READ getBaseSizeX WRITE setBaseSizeX NOTIFY dirty) - Q_PROPERTY(float baseSizeY READ getBaseSizeY WRITE setBaseSizeY NOTIFY dirty) - Q_PROPERTY(float baseSizeZ READ getBaseSizeZ WRITE setBaseSizeZ NOTIFY dirty) - Q_PROPERTY(float baseLevel READ getBaseLevel WRITE setBaseLevel NOTIFY dirty) - Q_PROPERTY(bool _isInverted READ isInverted WRITE setInverted NOTIFY dirty) - Q_PROPERTY(float noiseSizeX READ getNoiseSizeX WRITE setNoiseSizeX NOTIFY dirty) - Q_PROPERTY(float noiseSizeY READ getNoiseSizeY WRITE setNoiseSizeY NOTIFY dirty) - Q_PROPERTY(float noiseSizeZ READ getNoiseSizeZ WRITE setNoiseSizeZ NOTIFY dirty) - Q_PROPERTY(float noiseLevel READ getNoiseLevel WRITE setNoiseLevel NOTIFY dirty) - Q_PROPERTY(float edgeWidth READ getEdgeWidth WRITE setEdgeWidth NOTIFY dirty) - Q_PROPERTY(float edgeInnerColorR READ getEdgeInnerColorR WRITE setEdgeInnerColorR NOTIFY dirty) - Q_PROPERTY(float edgeInnerColorG READ getEdgeInnerColorG WRITE setEdgeInnerColorG NOTIFY dirty) - Q_PROPERTY(float edgeInnerColorB READ getEdgeInnerColorB WRITE setEdgeInnerColorB NOTIFY dirty) - Q_PROPERTY(float edgeInnerIntensity READ getEdgeInnerIntensity WRITE setEdgeInnerIntensity NOTIFY dirty) - Q_PROPERTY(float edgeOuterColorR READ getEdgeOuterColorR WRITE setEdgeOuterColorR NOTIFY dirty) - Q_PROPERTY(float edgeOuterColorG READ getEdgeOuterColorG WRITE setEdgeOuterColorG NOTIFY dirty) - Q_PROPERTY(float edgeOuterColorB READ getEdgeOuterColorB WRITE setEdgeOuterColorB NOTIFY dirty) - Q_PROPERTY(float edgeOuterIntensity READ getEdgeOuterIntensity WRITE setEdgeOuterIntensity NOTIFY dirty) - Q_PROPERTY(int timing READ getTiming WRITE setTiming NOTIFY dirty) - Q_PROPERTY(float noiseSpeedX READ getNoiseSpeedX WRITE setNoiseSpeedX NOTIFY dirty) - Q_PROPERTY(float noiseSpeedY READ getNoiseSpeedY WRITE setNoiseSpeedY NOTIFY dirty) - Q_PROPERTY(float noiseSpeedZ READ getNoiseSpeedZ WRITE setNoiseSpeedZ NOTIFY dirty) - Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) - Q_PROPERTY(bool manualFade MEMBER manualFade NOTIFY dirty) - Q_PROPERTY(float manualThreshold MEMBER manualThreshold NOTIFY dirty) - -public: - - enum Category { - ELEMENT_ENTER_LEAVE_DOMAIN = 0, - BUBBLE_ISECT_OWNER, - BUBBLE_ISECT_TRESPASSER, - USER_ENTER_LEAVE_DOMAIN, - AVATAR_CHANGE, - - // Don't forget to modify Fade.slh to reflect the change in number of categories - CATEGORY_COUNT, - }; - - enum Timing { - LINEAR, - EASE_IN, - EASE_OUT, - EASE_IN_OUT, - - TIMING_COUNT - }; - - FadeConfig(); - - void setEditedCategory(int value); - - void setDuration(float value); - float getDuration() const; - - void setBaseSizeX(float value); - float getBaseSizeX() const; - - void setBaseSizeY(float value); - float getBaseSizeY() const; - - void setBaseSizeZ(float value); - float getBaseSizeZ() const; - - void setBaseLevel(float value); - float getBaseLevel() const { return events[editedCategory].baseLevel; } - - void setInverted(bool value); - bool isInverted() const; - - void setNoiseSizeX(float value); - float getNoiseSizeX() const; - - void setNoiseSizeY(float value); - float getNoiseSizeY() const; - - void setNoiseSizeZ(float value); - float getNoiseSizeZ() const; - - void setNoiseLevel(float value); - float getNoiseLevel() const { return events[editedCategory].noiseLevel; } - - void setNoiseSpeedX(float value); - float getNoiseSpeedX() const; - - void setNoiseSpeedY(float value); - float getNoiseSpeedY() const; - - void setNoiseSpeedZ(float value); - float getNoiseSpeedZ() const; - - void setEdgeWidth(float value); - float getEdgeWidth() const; - - void setEdgeInnerColorR(float value); - float getEdgeInnerColorR() const { return events[editedCategory].edgeInnerColor.r; } - - void setEdgeInnerColorG(float value); - float getEdgeInnerColorG() const { return events[editedCategory].edgeInnerColor.g; } - - void setEdgeInnerColorB(float value); - float getEdgeInnerColorB() const { return events[editedCategory].edgeInnerColor.b; } - - void setEdgeInnerIntensity(float value); - float getEdgeInnerIntensity() const { return events[editedCategory].edgeInnerColor.a; } - - void setEdgeOuterColorR(float value); - float getEdgeOuterColorR() const { return events[editedCategory].edgeOuterColor.r; } - - void setEdgeOuterColorG(float value); - float getEdgeOuterColorG() const { return events[editedCategory].edgeOuterColor.g; } - - void setEdgeOuterColorB(float value); - float getEdgeOuterColorB() const { return events[editedCategory].edgeOuterColor.b; } - - void setEdgeOuterIntensity(float value); - float getEdgeOuterIntensity() const { return events[editedCategory].edgeOuterColor.a; } - - void setTiming(int value); - int getTiming() const { return events[editedCategory].timing; } - - struct Event { - glm::vec4 edgeInnerColor; - glm::vec4 edgeOuterColor; - glm::vec3 noiseSize; - glm::vec3 noiseSpeed; - glm::vec3 baseSize; - float noiseLevel; - float baseLevel; - float duration; - float edgeWidth; - int timing; - bool isInverted; - }; - - Event events[CATEGORY_COUNT]; - int editedCategory{ ELEMENT_ENTER_LEAVE_DOMAIN }; - float threshold{ 0.f }; - float manualThreshold{ 0.f }; - bool manualFade{ false }; - - Q_INVOKABLE void save() const; - Q_INVOKABLE void load(); - - static QString eventNames[CATEGORY_COUNT]; - -signals: - - void dirty(); - void dirtyCategory(); - -}; - -class FadeEditJob { - -public: - - using Config = FadeEditConfig; - using Input = render::VaryingSet2; - using JobModel = render::Job::ModelI; - - FadeEditJob() {} - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs); - -private: - - bool _isEditEnabled{ false }; - render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID }; - - render::ItemID findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; -}; - -class FadeJob { - -public: - - using Config = FadeConfig; - using Output = FadeConfig::Category; - using JobModel = render::Job::ModelO; - - FadeJob(); - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, FadeJob::Output& output); + void build(render::Task::TaskConcept& task, const task::Varying& editableItems); render::ShapePipeline::BatchSetter getBatchSetter() const; render::ShapePipeline::ItemSetter getItemSetter() const; private: - static const FadeConfig::Category transitionToCategory[render::Transition::TYPE_COUNT]; + gpu::BufferView _configurations; + gpu::TexturePointer _maskMap; -#include "Fade_shared.slh" + explicit FadeEffect(); + virtual ~FadeEffect() { } - struct FadeConfiguration - { - FadeParameters parameters[FadeConfig::CATEGORY_COUNT]; - }; - - gpu::StructBuffer _configurations; - gpu::TexturePointer _fadeMaskMap; - float _thresholdScale[FadeConfig::CATEGORY_COUNT]; - uint64_t _previousTime{ 0 }; - - void update(const Config& config, const render::ScenePointer& scene, render::Transition& transition, const double deltaTime) const; - static float computeElementEnterRatio(double time, const double period, FadeConfig::Timing timing); - - const render::Item* findNearestItem(const render::RenderContextPointer& renderContext, const render::Varying& input, float& minIsectDistance) const; }; - #endif // hifi_render_utils_FadeEffect_h diff --git a/libraries/render-utils/src/FadeEffectJobs.cpp b/libraries/render-utils/src/FadeEffectJobs.cpp new file mode 100644 index 0000000000..242d26a445 --- /dev/null +++ b/libraries/render-utils/src/FadeEffectJobs.cpp @@ -0,0 +1,720 @@ +// +// FadeEffectJobs.cpp + +// Created by Olivier Prat on 07/07/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "FadeEffectJobs.h" +#include "render/Logging.h" +#include "render/TransitionStage.h" + +#include +#include +#include + +#include + +#include + +#define FADE_MIN_SCALE 0.001 +#define FADE_MAX_SCALE 10000.0 +#define FADE_MAX_SPEED 50.f + +inline float parameterToValuePow(float parameter, const double minValue, const double maxOverMinValue) { + return (float)(minValue * pow(maxOverMinValue, double(parameter))); +} + +inline float valueToParameterPow(float value, const double minValue, const double maxOverMinValue) { + return (float)(log(double(value) / minValue) / log(maxOverMinValue)); +} + +void FadeEditJob::configure(const Config& config) { + _isEditEnabled = config.editFade; +} + +void FadeEditJob::run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs) { + auto scene = renderContext->_scene; + + if (_isEditEnabled) { + float minIsectDistance = std::numeric_limits::max(); + auto& itemBounds = inputs.get0(); + auto editedItem = findNearestItem(renderContext, itemBounds, minIsectDistance); + render::Transaction transaction; + bool hasTransaction{ false }; + + if (editedItem != _editedItem && render::Item::isValidID(_editedItem)) { + // Remove transition from previously edited item as we've changed edited item + hasTransaction = true; + transaction.removeTransitionFromItem(_editedItem); + } + _editedItem = editedItem; + + if (render::Item::isValidID(_editedItem)) { + static const render::Transition::Type categoryToTransition[FADE_CATEGORY_COUNT] = { + render::Transition::ELEMENT_ENTER_DOMAIN, + render::Transition::BUBBLE_ISECT_OWNER, + render::Transition::BUBBLE_ISECT_TRESPASSER, + render::Transition::USER_ENTER_DOMAIN, + render::Transition::AVATAR_CHANGE + }; + + auto transitionType = categoryToTransition[inputs.get1()]; + + transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) { + if (transition == nullptr || transition->isFinished || transition->eventType!=transitionType) { + // Relaunch transition + render::Transaction transaction; + transaction.addTransitionToItem(id, transitionType); + scene->enqueueTransaction(transaction); + } + }); + hasTransaction = true; + } + + if (hasTransaction) { + scene->enqueueTransaction(transaction); + } + } + else if (render::Item::isValidID(_editedItem)) { + // Remove transition from previously edited item as we've disabled fade edition + render::Transaction transaction; + transaction.removeTransitionFromItem(_editedItem); + scene->enqueueTransaction(transaction); + _editedItem = render::Item::INVALID_ITEM_ID; + } +} + +render::ItemID FadeEditJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { + const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); + const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + BoxFace face; + glm::vec3 normal; + float isectDistance; + render::ItemID nearestItem = render::Item::INVALID_ITEM_ID; + const float minDistance = 2.f; + + for (const auto& itemBound : inputs) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + if (isectDistance>minDistance && isectDistance < minIsectDistance) { + nearestItem = itemBound.id; + minIsectDistance = isectDistance; + } + } + } + return nearestItem; +} + +FadeConfig::FadeConfig() +{ + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 0.75f, 0.75f, 0.75f }; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseLevel = 1.f; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].noiseSpeed = glm::vec3{ 0.0f, 0.0f, 0.0f }; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].timing = FadeConfig::LINEAR; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].baseSize = glm::vec3{ 1.0f, 1.0f, 1.0f }; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].baseLevel = 0.f; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].isInverted = false; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].duration = 4.f; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].edgeWidth = 0.1f; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].edgeInnerColor = glm::vec4{ 78.f / 255.f, 215.f / 255.f, 255.f / 255.f, 0.0f }; + events[FADE_ELEMENT_ENTER_LEAVE_DOMAIN].edgeOuterColor = glm::vec4{ 78.f / 255.f, 215.f / 255.f, 255.f / 255.f, 1.0f }; + + events[FADE_BUBBLE_ISECT_OWNER].noiseSize = glm::vec3{ 1.5f, 1.0f / 25.f, 0.5f }; + events[FADE_BUBBLE_ISECT_OWNER].noiseLevel = 0.37f; + events[FADE_BUBBLE_ISECT_OWNER].noiseSpeed = glm::vec3{ 1.0f, 0.2f, 1.0f }; + events[FADE_BUBBLE_ISECT_OWNER].timing = FadeConfig::LINEAR; + events[FADE_BUBBLE_ISECT_OWNER].baseSize = glm::vec3{ 2.0f, 2.0f, 2.0f }; + events[FADE_BUBBLE_ISECT_OWNER].baseLevel = 1.f; + events[FADE_BUBBLE_ISECT_OWNER].isInverted = false; + events[FADE_BUBBLE_ISECT_OWNER].duration = 4.f; + events[FADE_BUBBLE_ISECT_OWNER].edgeWidth = 0.02f; + events[FADE_BUBBLE_ISECT_OWNER].edgeInnerColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 1.0f }; + events[FADE_BUBBLE_ISECT_OWNER].edgeOuterColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 2.0f }; + + events[FADE_BUBBLE_ISECT_TRESPASSER].noiseSize = glm::vec3{ 0.5f, 1.0f / 25.f, 0.5f }; + events[FADE_BUBBLE_ISECT_TRESPASSER].noiseLevel = 1.f; + events[FADE_BUBBLE_ISECT_TRESPASSER].noiseSpeed = glm::vec3{ 1.0f, -5.f, 1.0f }; + events[FADE_BUBBLE_ISECT_TRESPASSER].timing = FadeConfig::LINEAR; + events[FADE_BUBBLE_ISECT_TRESPASSER].baseSize = glm::vec3{ 2.0f, 2.0f, 2.0f }; + events[FADE_BUBBLE_ISECT_TRESPASSER].baseLevel = 0.f; + events[FADE_BUBBLE_ISECT_TRESPASSER].isInverted = false; + events[FADE_BUBBLE_ISECT_TRESPASSER].duration = 4.f; + events[FADE_BUBBLE_ISECT_TRESPASSER].edgeWidth = 0.025f; + events[FADE_BUBBLE_ISECT_TRESPASSER].edgeInnerColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 1.0f }; + events[FADE_BUBBLE_ISECT_TRESPASSER].edgeOuterColor = glm::vec4{ 31.f / 255.f, 198.f / 255.f, 166.f / 255.f, 2.0f }; + + events[FADE_USER_ENTER_LEAVE_DOMAIN].noiseSize = glm::vec3{ 10.f, 0.01f, 10.0f }; + events[FADE_USER_ENTER_LEAVE_DOMAIN].noiseLevel = 0.3f; + events[FADE_USER_ENTER_LEAVE_DOMAIN].noiseSpeed = glm::vec3{ 0.0f, -0.5f, 0.0f }; + events[FADE_USER_ENTER_LEAVE_DOMAIN].timing = FadeConfig::LINEAR; + events[FADE_USER_ENTER_LEAVE_DOMAIN].baseSize = glm::vec3{ 10000.f, 1.0f, 10000.0f }; + events[FADE_USER_ENTER_LEAVE_DOMAIN].baseLevel = 1.f; + events[FADE_USER_ENTER_LEAVE_DOMAIN].isInverted = true; + events[FADE_USER_ENTER_LEAVE_DOMAIN].duration = 2.f; + events[FADE_USER_ENTER_LEAVE_DOMAIN].edgeWidth = 0.229f; + events[FADE_USER_ENTER_LEAVE_DOMAIN].edgeInnerColor = glm::vec4{ 1.f, 0.63f, 0.13f, 0.5f }; + events[FADE_USER_ENTER_LEAVE_DOMAIN].edgeOuterColor = glm::vec4{ 1.f, 1.f, 1.f, 1.0f }; + + events[FADE_AVATAR_CHANGE].noiseSize = glm::vec3{ 0.4f, 0.4f, 0.4f }; + events[FADE_AVATAR_CHANGE].noiseLevel = 1.f; + events[FADE_AVATAR_CHANGE].noiseSpeed = glm::vec3{ 0.0f, 0.0f, 0.0f }; + events[FADE_AVATAR_CHANGE].timing = FadeConfig::LINEAR; + events[FADE_AVATAR_CHANGE].baseSize = glm::vec3{ 0.4f, 0.4f, 0.4f }; + events[FADE_AVATAR_CHANGE].baseLevel = 1.f; + events[FADE_AVATAR_CHANGE].isInverted = false; + events[FADE_AVATAR_CHANGE].duration = 3.f; + events[FADE_AVATAR_CHANGE].edgeWidth = 0.05f; + events[FADE_AVATAR_CHANGE].edgeInnerColor = glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f }; + events[FADE_AVATAR_CHANGE].edgeOuterColor = glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f }; +} + +void FadeConfig::setEditedCategory(int value) { + assert(value < CATEGORY_COUNT); + editedCategory = std::min(FADE_CATEGORY_COUNT, value); + emit dirtyCategory(); + emit dirty(); +} + +void FadeConfig::setDuration(float value) { + events[editedCategory].duration = value; + emit dirty(); +} + +float FadeConfig::getDuration() const { + return events[editedCategory].duration; +} + +void FadeConfig::setBaseSizeX(float value) { + events[editedCategory].baseSize.x = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE/ FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getBaseSizeX() const { + return valueToParameterPow(events[editedCategory].baseSize.x, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setBaseSizeY(float value) { + events[editedCategory].baseSize.y = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getBaseSizeY() const { + return valueToParameterPow(events[editedCategory].baseSize.y, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setBaseSizeZ(float value) { + events[editedCategory].baseSize.z = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getBaseSizeZ() const { + return valueToParameterPow(events[editedCategory].baseSize.z, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setBaseLevel(float value) { + events[editedCategory].baseLevel = value; + emit dirty(); +} + +void FadeConfig::setInverted(bool value) { + events[editedCategory].isInverted = value; + emit dirty(); +} + +bool FadeConfig::isInverted() const { + return events[editedCategory].isInverted; +} + +void FadeConfig::setNoiseSizeX(float value) { + events[editedCategory].noiseSize.x = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getNoiseSizeX() const { + return valueToParameterPow(events[editedCategory].noiseSize.x, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setNoiseSizeY(float value) { + events[editedCategory].noiseSize.y = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getNoiseSizeY() const { + return valueToParameterPow(events[editedCategory].noiseSize.y, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setNoiseSizeZ(float value) { + events[editedCategory].noiseSize.z = parameterToValuePow(value, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); + emit dirty(); +} + +float FadeConfig::getNoiseSizeZ() const { + return valueToParameterPow(events[editedCategory].noiseSize.z, FADE_MIN_SCALE, FADE_MAX_SCALE / FADE_MIN_SCALE); +} + +void FadeConfig::setNoiseLevel(float value) { + events[editedCategory].noiseLevel = value; + emit dirty(); +} + +void FadeConfig::setNoiseSpeedX(float value) { + events[editedCategory].noiseSpeed.x = powf(value, 3.f)*FADE_MAX_SPEED; + emit dirty(); +} + +float FadeConfig::getNoiseSpeedX() const { + return powf(events[editedCategory].noiseSpeed.x / FADE_MAX_SPEED, 1.f / 3.f); +} + +void FadeConfig::setNoiseSpeedY(float value) { + events[editedCategory].noiseSpeed.y = powf(value, 3.f)*FADE_MAX_SPEED; + emit dirty(); +} + +float FadeConfig::getNoiseSpeedY() const { + return powf(events[editedCategory].noiseSpeed.y / FADE_MAX_SPEED, 1.f / 3.f); +} + +void FadeConfig::setNoiseSpeedZ(float value) { + events[editedCategory].noiseSpeed.z = powf(value, 3.f)*FADE_MAX_SPEED; + emit dirty(); +} + +float FadeConfig::getNoiseSpeedZ() const { + return powf(events[editedCategory].noiseSpeed.z / FADE_MAX_SPEED, 1.f / 3.f); +} + +void FadeConfig::setEdgeWidth(float value) { + events[editedCategory].edgeWidth = value * value; + emit dirty(); +} + +float FadeConfig::getEdgeWidth() const { + return sqrtf(events[editedCategory].edgeWidth); +} + +void FadeConfig::setEdgeInnerColorR(float value) { + events[editedCategory].edgeInnerColor.r = value; + emit dirty(); +} + +void FadeConfig::setEdgeInnerColorG(float value) { + events[editedCategory].edgeInnerColor.g = value; + emit dirty(); +} + +void FadeConfig::setEdgeInnerColorB(float value) { + events[editedCategory].edgeInnerColor.b = value; + emit dirty(); +} + +void FadeConfig::setEdgeInnerIntensity(float value) { + events[editedCategory].edgeInnerColor.a = value; + emit dirty(); +} + +void FadeConfig::setEdgeOuterColorR(float value) { + events[editedCategory].edgeOuterColor.r = value; + emit dirty(); +} + +void FadeConfig::setEdgeOuterColorG(float value) { + events[editedCategory].edgeOuterColor.g = value; + emit dirty(); +} + +void FadeConfig::setEdgeOuterColorB(float value) { + events[editedCategory].edgeOuterColor.b = value; + emit dirty(); +} + +void FadeConfig::setEdgeOuterIntensity(float value) { + events[editedCategory].edgeOuterColor.a = value; + emit dirty(); +} + +void FadeConfig::setTiming(int value) { + assert(value < TIMING_COUNT); + events[editedCategory].timing = value; + emit dirty(); +} + +QString FadeConfig::eventNames[FADE_CATEGORY_COUNT] = { + "element_enter_leave_domain", + "bubble_isect_owner", + "bubble_isect_trespasser", + "user_enter_leave_domain", + "avatar_change", +}; + +void FadeConfig::save() const { + assert(editedCategory < FADE_CATEGORY_COUNT); + QJsonObject lProperties; + const QString configFile = "config/" + eventNames[editedCategory] + ".json"; + QUrl path(PathUtils::resourcesPath() + configFile); + QFile file(path.toString()); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + qWarning() << "Fade event configuration file " << path << " cannot be opened"; + } + else { + const auto& event = events[editedCategory]; + + lProperties["edgeInnerColor"] = QJsonArray{ event.edgeInnerColor.r, event.edgeInnerColor.g, event.edgeInnerColor.b, event.edgeInnerColor.a }; + lProperties["edgeOuterColor"] = QJsonArray{ event.edgeOuterColor.r, event.edgeOuterColor.g, event.edgeOuterColor.b, event.edgeOuterColor.a }; + lProperties["noiseSize"] = QJsonArray{ event.noiseSize.x, event.noiseSize.y, event.noiseSize.z }; + lProperties["noiseSpeed"] = QJsonArray{ event.noiseSpeed.x, event.noiseSpeed.y, event.noiseSpeed.z }; + lProperties["baseSize"] = QJsonArray{ event.baseSize.x, event.baseSize.y, event.baseSize.z }; + lProperties["noiseLevel"] = event.noiseLevel; + lProperties["baseLevel"] = event.baseLevel; + lProperties["duration"] = event.duration; + lProperties["edgeWidth"] = event.edgeWidth; + lProperties["timing"] = event.timing; + lProperties["isInverted"] = event.isInverted; + + file.write( QJsonDocument(lProperties).toJson() ); + file.close(); + } +} + +void FadeConfig::load() { + const QString configFile = "config/" + eventNames[editedCategory] + ".json"; + + QUrl path(PathUtils::resourcesPath() + configFile); + QFile file(path.toString()); + if (!file.exists()) { + qWarning() << "Fade event configuration file " << path << " does not exist"; + } + else if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Fade event configuration file " << path << " cannot be opened"; + } + else { + QString fileData = file.readAll(); + file.close(); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(fileData.toUtf8(), &error); + if (error.error == error.NoError) { + QJsonObject jsonObject = doc.object(); + QJsonValue value; + auto& event = events[editedCategory]; + + qCDebug(renderlogging) << "Fade event configuration file" << path << "loaded"; + + value = jsonObject["edgeInnerColor"]; + if (value.isArray()) { + QJsonArray data = value.toArray(); + + if (data.size() < 4) { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4"; + } + else { + event.edgeInnerColor.r = (float)data.at(0).toDouble(); + event.edgeInnerColor.g = (float)data.at(1).toDouble(); + event.edgeInnerColor.b = (float)data.at(2).toDouble(); + event.edgeInnerColor.a = (float)data.at(3).toDouble(); + } + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeInnerColor' field. Expected array of size 4"; + } + + value = jsonObject["edgeOuterColor"]; + if (value.isArray()) { + QJsonArray data = value.toArray(); + + if (data.size() < 4) { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4"; + } + else { + event.edgeOuterColor.r = (float)data.at(0).toDouble(); + event.edgeOuterColor.g = (float)data.at(1).toDouble(); + event.edgeOuterColor.b = (float)data.at(2).toDouble(); + event.edgeOuterColor.a = (float)data.at(3).toDouble(); + } + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeOuterColor' field. Expected array of size 4"; + } + + value = jsonObject["noiseSize"]; + if (value.isArray()) { + QJsonArray data = value.toArray(); + + if (data.size() < 3) { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3"; + } + else { + event.noiseSize.x = (float)data.at(0).toDouble(); + event.noiseSize.y = (float)data.at(1).toDouble(); + event.noiseSize.z = (float)data.at(2).toDouble(); + } + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSize' field. Expected array of size 3"; + } + + value = jsonObject["noiseSpeed"]; + if (value.isArray()) { + QJsonArray data = value.toArray(); + + if (data.size() < 3) { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3"; + } + else { + event.noiseSpeed.x = (float)data.at(0).toDouble(); + event.noiseSpeed.y = (float)data.at(1).toDouble(); + event.noiseSpeed.z = (float)data.at(2).toDouble(); + } + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseSpeed' field. Expected array of size 3"; + } + + value = jsonObject["baseSize"]; + if (value.isArray()) { + QJsonArray data = value.toArray(); + + if (data.size() < 3) { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3"; + } + else { + event.baseSize.x = (float)data.at(0).toDouble(); + event.baseSize.y = (float)data.at(1).toDouble(); + event.baseSize.z = (float)data.at(2).toDouble(); + } + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseSize' field. Expected array of size 3"; + } + + value = jsonObject["noiseLevel"]; + if (value.isDouble()) { + event.noiseLevel = (float)value.toDouble(); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'noiseLevel' field. Expected float value"; + } + + value = jsonObject["baseLevel"]; + if (value.isDouble()) { + event.baseLevel = (float)value.toDouble(); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'baseLevel' field. Expected float value"; + } + + value = jsonObject["duration"]; + if (value.isDouble()) { + event.duration = (float)value.toDouble(); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'duration' field. Expected float value"; + } + + value = jsonObject["edgeWidth"]; + if (value.isDouble()) { + event.edgeWidth = std::min(1.f, std::max(0.f, (float)value.toDouble())); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'edgeWidth' field. Expected float value"; + } + + value = jsonObject["timing"]; + if (value.isDouble()) { + event.timing = std::max(0, std::min(TIMING_COUNT - 1, value.toInt())); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'timing' field. Expected integer value"; + } + + value = jsonObject["isInverted"]; + if (value.isBool()) { + event.isInverted = value.toBool(); + } + else { + qWarning() << "Fade event configuration file " << path << " contains an invalid 'isInverted' field. Expected boolean value"; + } + + emit dirty(); + } + else { + qWarning() << "Fade event configuration file" << path << "failed to load:" << + error.errorString() << "at offset" << error.offset; + } + } +} + +FadeJob::FadeJob() { + _previousTime = usecTimestampNow(); +} + +void FadeJob::configure(const Config& config) { + auto& configurations = _configurations.edit(); + + for (auto i = 0; i < FADE_CATEGORY_COUNT; i++) { + auto& eventParameters = configurations.parameters[i]; + const auto& eventConfig = config.events[i]; + + eventParameters._baseLevel = eventConfig.baseLevel; + eventParameters._noiseInvSizeAndLevel.x = 1.f / eventConfig.noiseSize.x; + eventParameters._noiseInvSizeAndLevel.y = 1.f / eventConfig.noiseSize.y; + eventParameters._noiseInvSizeAndLevel.z = 1.f / eventConfig.noiseSize.z; + eventParameters._noiseInvSizeAndLevel.w = eventConfig.noiseLevel; + eventParameters._isInverted = eventConfig.isInverted & 1; + eventParameters._edgeWidthInvWidth.x = eventConfig.edgeWidth; + eventParameters._edgeWidthInvWidth.y = 1.f / eventParameters._edgeWidthInvWidth.x; + eventParameters._innerEdgeColor = eventConfig.edgeInnerColor; + eventParameters._outerEdgeColor = eventConfig.edgeOuterColor; + _thresholdScale[i] = 1.f + (eventParameters._edgeWidthInvWidth.x + std::max(0.f, (eventConfig.noiseLevel + eventConfig.baseLevel)*0.5f - 0.5f)); + } +} + +void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Output& output) { + Config* jobConfig = static_cast(renderContext->jobConfig.get()); + auto scene = renderContext->args->_scene; + auto transitionStage = scene->getStage(render::TransitionStage::getName()); + uint64_t now = usecTimestampNow(); + const double deltaTime = (int64_t(now) - int64_t(_previousTime)) / double(USECS_PER_SECOND); + bool isFirstItem = true; + + output = (FadeCategory) jobConfig->editedCategory; + + // And now update fade effect + for (auto transitionId : *transitionStage) { + auto& state = transitionStage->editTransition(transitionId); + update(*jobConfig, scene, state, deltaTime); + if (isFirstItem) { + jobConfig->setProperty("threshold", state.threshold); + isFirstItem = false; + } + } + _previousTime = now; +} + +const FadeCategory FadeJob::transitionToCategory[render::Transition::TYPE_COUNT] = { + FADE_ELEMENT_ENTER_LEAVE_DOMAIN, + FADE_ELEMENT_ENTER_LEAVE_DOMAIN, + FADE_BUBBLE_ISECT_OWNER, + FADE_BUBBLE_ISECT_TRESPASSER, + FADE_USER_ENTER_LEAVE_DOMAIN, + FADE_USER_ENTER_LEAVE_DOMAIN, + FADE_AVATAR_CHANGE +}; + +void FadeJob::update(const Config& config, const render::ScenePointer& scene, render::Transition& transition, const double deltaTime) const { + const auto fadeCategory = transitionToCategory[transition.eventType]; + auto& eventConfig = config.events[fadeCategory]; + auto& item = scene->getItem(transition.itemId); + const double eventDuration = (double)eventConfig.duration; + const FadeConfig::Timing timing = (FadeConfig::Timing) eventConfig.timing; + + if (item.exist()) { + auto aabb = item.getBound(); + if (render::Item::isValidID(transition.boundItemId)) { + auto& boundItem = scene->getItem(transition.boundItemId); + if (boundItem.exist()) { + aabb = boundItem.getBound(); + } + } + auto& dimensions = aabb.getDimensions(); + + assert(timing < FadeConfig::TIMING_COUNT); + + transition.noiseOffset = aabb.calcCenter(); + transition.baseInvSize.x = 1.f / eventConfig.baseSize.x; + transition.baseInvSize.y = 1.f / eventConfig.baseSize.y; + transition.baseInvSize.z = 1.f / eventConfig.baseSize.z; + + switch (transition.eventType) { + case render::Transition::ELEMENT_ENTER_DOMAIN: + case render::Transition::ELEMENT_LEAVE_DOMAIN: + { + transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing); + transition.baseOffset = transition.noiseOffset; + transition.baseInvSize.x = 1.f / dimensions.x; + transition.baseInvSize.y = 1.f / dimensions.y; + transition.baseInvSize.z = 1.f / dimensions.z; + transition.isFinished += (transition.threshold >= 1.f) & 1; + if (transition.eventType == render::Transition::ELEMENT_ENTER_DOMAIN) { + transition.threshold = 1.f - transition.threshold; + } + } + break; + + case render::Transition::BUBBLE_ISECT_OWNER: + { + transition.threshold = 0.5f; + transition.baseOffset = transition.noiseOffset; + } + break; + + case render::Transition::BUBBLE_ISECT_TRESPASSER: + { + transition.threshold = 0.5f; + transition.baseOffset = transition.noiseOffset; + } + break; + + case render::Transition::USER_ENTER_DOMAIN: + case render::Transition::USER_LEAVE_DOMAIN: + { + transition.threshold = computeElementEnterRatio(transition.time, eventDuration, timing); + transition.baseOffset = transition.noiseOffset - dimensions.y / 2.f; + transition.baseInvSize.y = 1.f / dimensions.y; + transition.isFinished += (transition.threshold >= 1.f) & 1; + if (transition.eventType == render::Transition::USER_LEAVE_DOMAIN) { + transition.threshold = 1.f - transition.threshold; + } + } + break; + + case render::Transition::AVATAR_CHANGE: + break; + + default: + assert(false); + } + } + + transition.noiseOffset += eventConfig.noiseSpeed * (float)transition.time; + if (config.manualFade) { + transition.threshold = config.manualThreshold; + } + transition.threshold = std::max(0.f, std::min(1.f, transition.threshold)); + transition.threshold = (transition.threshold - 0.5f)*_thresholdScale[fadeCategory] + 0.5f; + transition.time += deltaTime; + + // If the transition is finished for more than a number of frames (here 3), garbage collect it. + if (transition.isFinished > 3) { + render::Transaction transaction; + transaction.removeTransitionFromItem(transition.itemId); + scene->enqueueTransaction(transaction); + } +} + +float FadeJob::computeElementEnterRatio(double time, const double period, FadeConfig::Timing timing) { + assert(period > 0.0); + float fadeAlpha = 1.0f; + const double INV_FADE_PERIOD = 1.0 / period; + double fraction = time * INV_FADE_PERIOD; + fraction = std::max(fraction, 0.0); + if (fraction < 1.0) { + switch (timing) { + default: + fadeAlpha = (float)fraction; + break; + case FadeConfig::EASE_IN: + fadeAlpha = (float)(fraction*fraction*fraction); + break; + case FadeConfig::EASE_OUT: + fadeAlpha = 1.f - (float)fraction; + fadeAlpha = 1.f- fadeAlpha*fadeAlpha*fadeAlpha; + break; + case FadeConfig::EASE_IN_OUT: + fadeAlpha = (float)(fraction*fraction*fraction*(fraction*(fraction * 6 - 15) + 10)); + break; + } + } + return fadeAlpha; +} diff --git a/libraries/render-utils/src/FadeEffectJobs.h b/libraries/render-utils/src/FadeEffectJobs.h new file mode 100644 index 0000000000..ca29f40b5c --- /dev/null +++ b/libraries/render-utils/src/FadeEffectJobs.h @@ -0,0 +1,248 @@ +// +// FadeEffectJobs.h + +// Created by Olivier Prat on 07/07/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_utils_FadeEffectJobs_h +#define hifi_render_utils_FadeEffectJobs_h + +#include "FadeEffect.h" + +#include +#include +#include +#include + +enum FadeCategory { + FADE_ELEMENT_ENTER_LEAVE_DOMAIN = 0, + FADE_BUBBLE_ISECT_OWNER, + FADE_BUBBLE_ISECT_TRESPASSER, + FADE_USER_ENTER_LEAVE_DOMAIN, + FADE_AVATAR_CHANGE, + + // Don't forget to modify Fade.slh to reflect the change in number of categories + FADE_CATEGORY_COUNT, +}; + +class FadeEditConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool editFade MEMBER editFade NOTIFY dirty) + +public: + + bool editFade{ false }; + +signals: + + void dirty(); +}; + +class FadeConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int editedCategory MEMBER editedCategory WRITE setEditedCategory NOTIFY dirtyCategory) + Q_PROPERTY(float duration READ getDuration WRITE setDuration NOTIFY dirty) + Q_PROPERTY(float baseSizeX READ getBaseSizeX WRITE setBaseSizeX NOTIFY dirty) + Q_PROPERTY(float baseSizeY READ getBaseSizeY WRITE setBaseSizeY NOTIFY dirty) + Q_PROPERTY(float baseSizeZ READ getBaseSizeZ WRITE setBaseSizeZ NOTIFY dirty) + Q_PROPERTY(float baseLevel READ getBaseLevel WRITE setBaseLevel NOTIFY dirty) + Q_PROPERTY(bool _isInverted READ isInverted WRITE setInverted NOTIFY dirty) + Q_PROPERTY(float noiseSizeX READ getNoiseSizeX WRITE setNoiseSizeX NOTIFY dirty) + Q_PROPERTY(float noiseSizeY READ getNoiseSizeY WRITE setNoiseSizeY NOTIFY dirty) + Q_PROPERTY(float noiseSizeZ READ getNoiseSizeZ WRITE setNoiseSizeZ NOTIFY dirty) + Q_PROPERTY(float noiseLevel READ getNoiseLevel WRITE setNoiseLevel NOTIFY dirty) + Q_PROPERTY(float edgeWidth READ getEdgeWidth WRITE setEdgeWidth NOTIFY dirty) + Q_PROPERTY(float edgeInnerColorR READ getEdgeInnerColorR WRITE setEdgeInnerColorR NOTIFY dirty) + Q_PROPERTY(float edgeInnerColorG READ getEdgeInnerColorG WRITE setEdgeInnerColorG NOTIFY dirty) + Q_PROPERTY(float edgeInnerColorB READ getEdgeInnerColorB WRITE setEdgeInnerColorB NOTIFY dirty) + Q_PROPERTY(float edgeInnerIntensity READ getEdgeInnerIntensity WRITE setEdgeInnerIntensity NOTIFY dirty) + Q_PROPERTY(float edgeOuterColorR READ getEdgeOuterColorR WRITE setEdgeOuterColorR NOTIFY dirty) + Q_PROPERTY(float edgeOuterColorG READ getEdgeOuterColorG WRITE setEdgeOuterColorG NOTIFY dirty) + Q_PROPERTY(float edgeOuterColorB READ getEdgeOuterColorB WRITE setEdgeOuterColorB NOTIFY dirty) + Q_PROPERTY(float edgeOuterIntensity READ getEdgeOuterIntensity WRITE setEdgeOuterIntensity NOTIFY dirty) + Q_PROPERTY(int timing READ getTiming WRITE setTiming NOTIFY dirty) + Q_PROPERTY(float noiseSpeedX READ getNoiseSpeedX WRITE setNoiseSpeedX NOTIFY dirty) + Q_PROPERTY(float noiseSpeedY READ getNoiseSpeedY WRITE setNoiseSpeedY NOTIFY dirty) + Q_PROPERTY(float noiseSpeedZ READ getNoiseSpeedZ WRITE setNoiseSpeedZ NOTIFY dirty) + Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) + Q_PROPERTY(bool manualFade MEMBER manualFade NOTIFY dirty) + Q_PROPERTY(float manualThreshold MEMBER manualThreshold NOTIFY dirty) + +public: + + enum Timing { + LINEAR, + EASE_IN, + EASE_OUT, + EASE_IN_OUT, + + TIMING_COUNT + }; + + FadeConfig(); + + void setEditedCategory(int value); + + void setDuration(float value); + float getDuration() const; + + void setBaseSizeX(float value); + float getBaseSizeX() const; + + void setBaseSizeY(float value); + float getBaseSizeY() const; + + void setBaseSizeZ(float value); + float getBaseSizeZ() const; + + void setBaseLevel(float value); + float getBaseLevel() const { return events[editedCategory].baseLevel; } + + void setInverted(bool value); + bool isInverted() const; + + void setNoiseSizeX(float value); + float getNoiseSizeX() const; + + void setNoiseSizeY(float value); + float getNoiseSizeY() const; + + void setNoiseSizeZ(float value); + float getNoiseSizeZ() const; + + void setNoiseLevel(float value); + float getNoiseLevel() const { return events[editedCategory].noiseLevel; } + + void setNoiseSpeedX(float value); + float getNoiseSpeedX() const; + + void setNoiseSpeedY(float value); + float getNoiseSpeedY() const; + + void setNoiseSpeedZ(float value); + float getNoiseSpeedZ() const; + + void setEdgeWidth(float value); + float getEdgeWidth() const; + + void setEdgeInnerColorR(float value); + float getEdgeInnerColorR() const { return events[editedCategory].edgeInnerColor.r; } + + void setEdgeInnerColorG(float value); + float getEdgeInnerColorG() const { return events[editedCategory].edgeInnerColor.g; } + + void setEdgeInnerColorB(float value); + float getEdgeInnerColorB() const { return events[editedCategory].edgeInnerColor.b; } + + void setEdgeInnerIntensity(float value); + float getEdgeInnerIntensity() const { return events[editedCategory].edgeInnerColor.a; } + + void setEdgeOuterColorR(float value); + float getEdgeOuterColorR() const { return events[editedCategory].edgeOuterColor.r; } + + void setEdgeOuterColorG(float value); + float getEdgeOuterColorG() const { return events[editedCategory].edgeOuterColor.g; } + + void setEdgeOuterColorB(float value); + float getEdgeOuterColorB() const { return events[editedCategory].edgeOuterColor.b; } + + void setEdgeOuterIntensity(float value); + float getEdgeOuterIntensity() const { return events[editedCategory].edgeOuterColor.a; } + + void setTiming(int value); + int getTiming() const { return events[editedCategory].timing; } + + struct Event { + glm::vec4 edgeInnerColor; + glm::vec4 edgeOuterColor; + glm::vec3 noiseSize; + glm::vec3 noiseSpeed; + glm::vec3 baseSize; + float noiseLevel; + float baseLevel; + float duration; + float edgeWidth; + int timing; + bool isInverted; + }; + + Event events[FADE_CATEGORY_COUNT]; + int editedCategory{ FADE_ELEMENT_ENTER_LEAVE_DOMAIN }; + float threshold{ 0.f }; + float manualThreshold{ 0.f }; + bool manualFade{ false }; + + Q_INVOKABLE void save() const; + Q_INVOKABLE void load(); + + static QString eventNames[FADE_CATEGORY_COUNT]; + +signals: + + void dirty(); + void dirtyCategory(); + +}; + +class FadeEditJob { + +public: + + using Config = FadeEditConfig; + using Input = render::VaryingSet2; + using JobModel = render::Job::ModelI; + + FadeEditJob() {} + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs); + +private: + + bool _isEditEnabled{ false }; + render::ItemID _editedItem{ render::Item::INVALID_ITEM_ID }; + + render::ItemID findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const; +}; + +class FadeJob { + +public: + + static const FadeCategory transitionToCategory[render::Transition::TYPE_COUNT]; + + using Config = FadeConfig; + using Output = FadeCategory; + using JobModel = render::Job::ModelO; + + FadeJob(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, FadeJob::Output& output); + + gpu::BufferView getConfigurationBuffer() const { return _configurations; } + +private: + +#include "Fade_shared.slh" + + struct FadeConfiguration + { + FadeParameters parameters[FADE_CATEGORY_COUNT]; + }; + using FadeConfigurationBuffer = gpu::StructBuffer; + + FadeConfigurationBuffer _configurations; + float _thresholdScale[FADE_CATEGORY_COUNT]; + uint64_t _previousTime{ 0 }; + + void update(const Config& config, const render::ScenePointer& scene, render::Transition& transition, const double deltaTime) const; + static float computeElementEnterRatio(double time, const double period, FadeConfig::Timing timing); + +}; + +#endif // hifi_render_utils_FadeEffectJobs_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index f6a715efa3..32903ad0d2 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -48,19 +48,21 @@ using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); +RenderDeferredTask::RenderDeferredTask() { + DependencyManager::set(); +} + void RenderDeferredTask::configure(const Config& config) { } void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { const auto& items = input.get(); - - auto editedFadeCategory = task.addJob("Fade"); - auto& fadeJob = task._jobs.back().get(); + auto fadeEffect = DependencyManager::get(); // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); - initDeferredPipelines(*shapePlumber, fadeJob.getBatchSetter(), fadeJob.getItemSetter()); + initDeferredPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemSetter()); // Extract opaques / transparents / lights / metas / overlays / background const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE]; @@ -72,8 +74,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren //const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; const auto& spatialSelection = items[1]; - const auto fadeEditInput = FadeEditJob::Input(opaques, editedFadeCategory).asVarying(); - task.addJob("FadeEdit", fadeEditInput); + fadeEffect->build(task, opaques); // Filter the non antialiaased overlays const int LAYER_NO_AA = 3; diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 65ae062a9a..e7575a1c95 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -185,7 +185,7 @@ public: using Config = RenderDeferredTaskConfig; using JobModel = render::Task::ModelI; - RenderDeferredTask() {} + RenderDeferredTask(); void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);