diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 2307b1be3b..e23d9e57a8 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -463,19 +463,15 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){ if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) { - // we have a node that already has these exact sockets - this can occur if a node - // is failing to connect to the domain - - // we'll re-use the existing node ID - // as long as the user hasn't changed their username (by logging in or logging out) - auto existingNodeData = static_cast(node->getLinkedData()); - - if (existingNodeData->getUsername() == username) { - qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); - existingNodeID = node->getUUID(); - return false; - } + // we have a node that already has these exact sockets + // this can occur if a node is failing to connect to the domain + + // remove the old node before adding the new node + qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); + existingNodeID = node->getUUID(); + return false; } + return true; }); diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 8d17658299..3bb7cfc8c5 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -60,6 +60,9 @@ Item { StatText { text: "Game Rate: " + root.gameLoopRate } + StatText { + text: "Physics Object Count: " + root.physicsObjectCount + } StatText { visible: root.expanded text: root.gameUpdateStats diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 60d23c94dd..e290531471 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1095,6 +1095,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Set File Logger Session UUID auto avatarManager = DependencyManager::get(); auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; + if (avatarManager) { + workload::SpacePointer space = getEntities()->getWorkloadSpace(); + avatarManager->setSpace(space); + } auto accountManager = DependencyManager::get(); _logger->setSessionID(accountManager->getSessionID()); @@ -2542,11 +2546,15 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - DependencyManager::get()->clearOtherAvatars(); - VectorOfMotionStates motionStates; - DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - DependencyManager::get()->deleteAllAvatars(); + auto avatarManager = DependencyManager::get(); + avatarManager->clearOtherAvatars(); + + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + + avatarManager->deleteAllAvatars(); _physicsEngine->setCharacterController(nullptr); @@ -5708,12 +5716,10 @@ void Application::update(float deltaTime) { t1 = std::chrono::high_resolution_clock::now(); - avatarManager->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - avatarManager->getObjectsToAddToPhysics(motionStates); - _physicsEngine->addObjects(motionStates); - avatarManager->getObjectsToChange(motionStates); - _physicsEngine->changeObjects(motionStates); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); myAvatar->prepareForPhysicsSimulation(); _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { @@ -6184,6 +6190,10 @@ bool Application::isHMDMode() const { return getActiveDisplayPlugin()->isHmd(); } +float Application::getNumCollisionObjects() const { + return _physicsEngine ? _physicsEngine->getNumCollisionObjects() : 0; +} + float Application::getTargetRenderFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); } QRect Application::getDesirableApplicationGeometry() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 742cf075f6..ae7e686f35 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -207,6 +207,7 @@ public: size_t getRenderFrameCount() const { return _renderFrameCount; } float getRenderLoopRate() const { return _renderLoopCounter.rate(); } + float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second float getFieldOfView() { return _fieldOfView.get(); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d4c74127c8..a6ba983ab5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -452,6 +452,9 @@ Menu::Menu() { }); } + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ComputeBlendshapes, 0, true, + DependencyManager::get().data(), SLOT(setComputeBlendshapes(bool))); + // Developer > Assets >>> // Menu item is not currently needed but code should be kept in case it proves useful again at some stage. //#define WANT_ASSET_MIGRATION diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4386bec5ae..c4ea3734f5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -222,6 +222,7 @@ namespace MenuOption { const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; const QString ForceCoarsePicking = "Force Coarse Picking"; + const QString ComputeBlendshapes = "Compute Blendshapes"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6fb35ec1da..9a7d8ef0c8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -44,7 +44,6 @@ #include "InterfaceLogging.h" #include "Menu.h" #include "MyAvatar.h" -#include "OtherAvatar.h" #include "SceneScriptingInterface.h" // 50 times per second - target is 45hz, but this helps account for any small deviations @@ -81,8 +80,25 @@ AvatarManager::AvatarManager(QObject* parent) : }); } +AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + + const auto otherAvatar = std::static_pointer_cast(avatar); + if (otherAvatar && _space) { + std::unique_lock lock(_spaceLock); + auto spaceIndex = _space->allocateID(); + otherAvatar->setSpaceIndex(spaceIndex); + workload::Sphere sphere(otherAvatar->getWorldPosition(), otherAvatar->getBoundingRadius()); + workload::Transaction transaction; + SpatiallyNestablePointer nestable = std::static_pointer_cast(otherAvatar); + transaction.reset(spaceIndex, sphere, workload::Owner(nestable)); + _space->enqueueTransaction(transaction); + } + return avatar; +} + AvatarManager::~AvatarManager() { - assert(_motionStates.empty()); + assert(_avatarsToChangeInPhysics.empty()); } void AvatarManager::init() { @@ -104,6 +120,11 @@ void AvatarManager::init() { } } +void AvatarManager::setSpace(workload::SpacePointer& space ) { + assert(!_space); + _space = space; +} + void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); @@ -192,20 +213,20 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t updateExpiry = startTime + UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; - bool physicsEnabled = qApp->isPhysicsEnabled(); - render::Transaction transaction; + render::Transaction renderTransaction; + workload::Transaction workloadTransaction; while (!sortedAvatars.empty()) { const SortableAvatar& sortData = sortedAvatars.top(); - const auto avatar = std::static_pointer_cast(sortData.getAvatar()); - const auto otherAvatar = std::static_pointer_cast(sortData.getAvatar()); + const auto avatar = std::static_pointer_cast(sortData.getAvatar()); + // TODO: to help us scale to more avatars it would be nice to not have to poll orb state here // if the geometry is loaded then turn off the orb if (avatar->getSkeletonModel()->isLoaded()) { // remove the orb if it is there - otherAvatar->removeOrb(); + avatar->removeOrb(); } else { - otherAvatar->updateOrbPosition(); + avatar->updateOrbPosition(); } bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); @@ -218,18 +239,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } - if (physicsEnabled && !avatar->isInPhysicsSimulation()) { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); - motionState->setMass(avatar->computeMass()); - avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); }); - _motionStates.insert(avatar.get(), motionState); - _motionStatesToAddToPhysics.insert(motionState); - } - } avatar->animateScaleChanges(deltaTime); const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; @@ -241,15 +250,16 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { numAvatarsUpdated++; } avatar->simulate(deltaTime, inView); - avatar->updateRenderItem(transaction); + avatar->updateRenderItem(renderTransaction); + avatar->updateSpaceProxy(workloadTransaction); avatar->setLastRenderUpdateTime(startTime); } else { // we've spent our full time budget --> bail on the rest of the avatar updates // --> more avatars may freeze until their priority trickles up - // --> some scale or fade animations may glitch + // --> some scale animations may glitch // --> some avatar velocity measurements may be a little off - // no time simulate, but we take the time to count how many were tragically missed + // no time to simulate, but we take the time to count how many were tragically missed bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; @@ -273,8 +283,16 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } if (_shouldRender) { - qApp->getMain3DScene()->enqueueTransaction(transaction); + qApp->getMain3DScene()->enqueueTransaction(renderTransaction); } + + if (!_spaceProxiesToDelete.empty() && _space) { + std::unique_lock lock(_spaceLock); + workloadTransaction.remove(_spaceProxiesToDelete); + _spaceProxiesToDelete.clear(); + } + _space->enqueueTransaction(workloadTransaction); + _numAvatarsUpdated = numAvatarsUpdated; _numAvatarsNotUpdated = numAVatarsNotUpdated; @@ -341,40 +359,85 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { QReadLocker locker(&_hashLock); QVector::iterator avatarItr = _avatarsToFade.begin(); const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; while (avatarItr != _avatarsToFade.end()) { auto avatar = std::static_pointer_cast(*avatarItr); avatar->updateFadingStatus(scene); if (!avatar->isFading()) { // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { - render::Transaction transaction; avatar->removeFromScene(*avatarItr, scene, transaction); - scene->enqueueTransaction(transaction); } avatarItr = _avatarsToFade.erase(avatarItr); } else { ++avatarItr; } } + scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::newSharedAvatar() { return AvatarSharedPointer(new OtherAvatar(qApp->thread()), [](OtherAvatar* ptr) { ptr->deleteLater(); }); } -void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { - AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); +void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) { + _avatarsToChangeInPhysics.insert(avatar); +} - // remove from physics - auto avatar = std::static_pointer_cast(removedAvatar); - avatar->setPhysicsCallback(nullptr); - AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); - if (itr != _motionStates.end()) { - AvatarMotionState* motionState = *itr; - _motionStatesToAddToPhysics.remove(motionState); - _motionStatesToRemoveFromPhysics.push_back(motionState); - _motionStates.erase(itr); +void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + SetOfOtherAvatars failedShapeBuilds; + for (auto avatar : _avatarsToChangeInPhysics) { + bool isInPhysics = avatar->isInPhysicsSimulation(); + if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) { + if (isInPhysics) { + transaction.objectsToRemove.push_back(avatar->_motionState); + avatar->_motionState = nullptr; + } else { + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + motionState->setMass(avatar->computeMass()); + avatar->_motionState = motionState; + transaction.objectsToAdd.push_back(motionState); + } else { + failedShapeBuilds.insert(avatar); + } + } + } else if (isInPhysics) { + transaction.objectsToChange.push_back(avatar->_motionState); + } } + _avatarsToChangeInPhysics.swap(failedShapeBuilds); +} + +void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToChange correspond to failed changes + // so we push them back onto _avatarsToChangeInPhysics + for (auto object : transaction.objectsToChange) { + AvatarMotionState* motionState = static_cast(object); + assert(motionState); + assert(motionState->_avatar); + _avatarsToChangeInPhysics.insert(motionState->_avatar); + } + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + delete object; + } + transaction.clear(); +} + +void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { + auto avatar = std::static_pointer_cast(removedAvatar); + { + std::unique_lock lock(_spaceLock); + _spaceProxiesToDelete.push_back(avatar->getSpaceIndex()); + } + AvatarHashMap::handleRemovedAvatar(avatar, removalReason); + + avatar->die(); + queuePhysicsChange(avatar); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); @@ -410,48 +473,15 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here - deleteMotionStates(); + assert(_avatarsToChangeInPhysics.empty()); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - auto avatar = std::static_pointer_cast(avatarIterator.value()); + auto avatar = std::static_pointer_cast(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); avatar->die(); - } -} - -void AvatarManager::deleteMotionStates() { - // delete motionstates that were removed from physics last frame - for (auto state : _motionStatesToDelete) { - delete state; - } - _motionStatesToDelete.clear(); -} - -void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { - deleteMotionStates(); - result = _motionStatesToRemoveFromPhysics; - _motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics); -} - -void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { - result.clear(); - for (auto motionState : _motionStatesToAddToPhysics) { - result.push_back(motionState); - } - _motionStatesToAddToPhysics.clear(); -} - -void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); - while (motionStateItr != _motionStates.end()) { - if ((*motionStateItr)->getIncomingDirtyFlags() != 0) { - result.push_back(*motionStateItr); - } - ++motionStateItr; + assert(!avatar->_motionState); } } @@ -598,7 +628,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic } if (sortedAvatars.size() > 1) { - static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first < right.first; }; std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); } @@ -691,7 +721,7 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector } if (sortedAvatars.size() > 1) { - static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first < right.first; }; std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index a9c04e8473..bcdfc064bd 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -12,6 +12,8 @@ #ifndef hifi_AvatarManager_h #define hifi_AvatarManager_h +#include + #include #include #include @@ -23,9 +25,11 @@ #include #include #include +#include #include "AvatarMotionState.h" #include "MyAvatar.h" +#include "OtherAvatar.h" using SortedAvatar = std::pair>; @@ -64,6 +68,7 @@ public: virtual ~AvatarManager(); void init(); + void setSpace(workload::SpacePointer& space ); std::shared_ptr getMyAvatar() { return _myAvatar; } glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); } @@ -94,6 +99,7 @@ public: void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); @@ -104,23 +110,21 @@ public: * @returns {number} */ Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; - + /**jsdoc * @function AvatarManager.getAvatarUpdateRate * @param {Uuid} sessionID * @param {string} [rateName=""] * @returns {number} */ - Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const; - + /**jsdoc * @function AvatarManager.getAvatarSimulationRate * @param {Uuid} sessionID * @param {string} [rateName=""] * @returns {number} */ - Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const; /**jsdoc @@ -155,7 +159,7 @@ public: */ // TODO: remove this HACK once we settle on optimal default sort coefficients Q_INVOKABLE float getAvatarSortCoefficient(const QString& name); - + /**jsdoc * @function AvatarManager.setAvatarSortCoefficient * @param {string} name @@ -177,14 +181,20 @@ public: float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } -public slots: + void queuePhysicsChange(const OtherAvatarPointer& avatar); + void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); +public slots: /**jsdoc * @function AvatarManager.updateAvatarRenderStatus * @param {boolean} shouldRenderAvatars */ void updateAvatarRenderStatus(bool shouldRenderAvatars); +protected: + AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; + private: explicit AvatarManager(QObject* parent = 0); explicit AvatarManager(const AvatarManager& other); @@ -192,16 +202,12 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - void deleteMotionStates(); void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; - using AvatarMotionStateMap = QMap; - AvatarMotionStateMap _motionStates; - VectorOfMotionStates _motionStatesToRemoveFromPhysics; - VectorOfMotionStates _motionStatesToDelete; - SetOfMotionStates _motionStatesToAddToPhysics; + using SetOfOtherAvatars = std::set; + SetOfOtherAvatars _avatarsToChangeInPhysics; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. @@ -214,6 +220,10 @@ private: float _avatarSimulationTime { 0.0f }; bool _shouldRender { true }; mutable int _identityRequestsSent { 0 }; + + mutable std::mutex _spaceLock; + workload::SpacePointer _space; + std::vector _spaceProxiesToDelete; }; #endif // hifi_AvatarManager_h diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 07e6b3f6b0..ca67f634c8 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -16,7 +16,7 @@ #include -AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { +AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; cacheShapeDiameter(); @@ -57,7 +57,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { // virtual and protected const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; - std::static_pointer_cast(_avatar)->computeShapeInfo(shapeInfo); + _avatar->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); } @@ -151,7 +151,7 @@ glm::vec3 AvatarMotionState::getObjectAngularVelocity() const { // virtual glm::vec3 AvatarMotionState::getObjectGravity() const { - return std::static_pointer_cast(_avatar)->getAcceleration(); + return _avatar->getAcceleration(); } // virtual @@ -176,7 +176,7 @@ void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma // virtual float AvatarMotionState::getMass() const { - return std::static_pointer_cast(_avatar)->computeMass(); + return _avatar->computeMass(); } void AvatarMotionState::cacheShapeDiameter() { diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index a458704b1a..2533c11d56 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -14,14 +14,14 @@ #include -#include #include #include +#include "OtherAvatar.h" class AvatarMotionState : public ObjectMotionState { public: - AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape); + AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape); virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; @@ -85,7 +85,7 @@ protected: virtual bool isReadyToComputeShape() const override { return true; } virtual const btCollisionShape* computeNewShape() override; - AvatarSharedPointer _avatar; + OtherAvatarPointer _avatar; float _diameter { 0.0f }; uint32_t _dirtyFlags; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 2061df6004..29ad5aed91 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -9,6 +9,8 @@ #include "OtherAvatar.h" #include "Application.h" +#include "AvatarMotionState.h" + OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = new Head(this); @@ -58,3 +60,38 @@ void OtherAvatar::createOrb() { _otherAvatarOrbMeshPlaceholder->setVisible(true); } } + +void OtherAvatar::setSpaceIndex(int32_t index) { + assert(_spaceIndex == -1); + _spaceIndex = index; +} + +void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { + if (_spaceIndex > -1) { + float approximateBoundingRadius = glm::length(getTargetScale()); + workload::Sphere sphere(getWorldPosition(), approximateBoundingRadius); + transaction.update(_spaceIndex, sphere); + } +} + +int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { + int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); + if (_moving && _motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); + } + return bytesRead; +} + +void OtherAvatar::setWorkloadRegion(uint8_t region) { + _workloadRegion = region; +} + +bool OtherAvatar::shouldBeInPhysicsSimulation() const { + return (_workloadRegion < workload::Region::R3 && !isDead()); +} + +void OtherAvatar::rebuildCollisionShape() { + if (_motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + } +} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index f33952b78b..ccfe42dbed 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -1,6 +1,6 @@ // -// Created by Bradley Austin Davis on 2017/04/27 -// Copyright 2013-2017 High Fidelity, Inc. +// Created by amantly 2018.06.26 +// Copyright 2018 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 @@ -9,10 +9,17 @@ #ifndef hifi_OtherAvatar_h #define hifi_OtherAvatar_h +#include + #include +#include + +#include "InterfaceLogging.h" #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" -#include "InterfaceLogging.h" + +class AvatarManager; +class AvatarMotionState; class OtherAvatar : public Avatar { public: @@ -24,9 +31,28 @@ public: void updateOrbPosition(); void removeOrb(); + void setSpaceIndex(int32_t index); + int32_t getSpaceIndex() const { return _spaceIndex; } + void updateSpaceProxy(workload::Transaction& transaction) const; + + int parseDataFromBuffer(const QByteArray& buffer) override; + + bool isInPhysicsSimulation() const { return _motionState != nullptr; } + void rebuildCollisionShape() override; + + void setWorkloadRegion(uint8_t region); + bool shouldBeInPhysicsSimulation() const; + + friend AvatarManager; + protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; + AvatarMotionState* _motionState { nullptr }; + int32_t _spaceIndex { -1 }; + uint8_t _workloadRegion { workload::Region::INVALID }; }; +using OtherAvatarPointer = std::shared_ptr; + #endif // hifi_OtherAvatar_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 16e2bb955f..8434545f22 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -122,6 +122,7 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(physicsObjectCount, qApp->getNumCollisionObjects()); STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 7845a540de..f344c7e13b 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -48,6 +48,7 @@ private: \ * @property {number} presentdroprate - Read-only. * @property {number} gameLoopRate - Read-only. * @property {number} avatarCount - Read-only. + * @property {number} physicsObjectCount - Read-only. * @property {number} updatedAvatarCount - Read-only. * @property {number} notUpdatedAvatarCount - Read-only. * @property {number} packetInCount - Read-only. @@ -203,6 +204,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, presentdroprate, 0) STATS_PROPERTY(int, gameLoopRate, 0) STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, physicsObjectCount, 0) STATS_PROPERTY(int, updatedAvatarCount, 0) STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) @@ -423,6 +425,13 @@ signals: */ void gameLoopRateChanged(); + /**jsdoc + * Trigered when + * @function Stats.numPhysicsBodiesChanged + * @returns {Signal} + */ + void physicsObjectCountChanged(); + /**jsdoc * Triggered when the value of the avatarCount property changes. * @function Stats.avatarCountChanged diff --git a/interface/src/workload/PhysicsBoundary.cpp b/interface/src/workload/PhysicsBoundary.cpp index 927121ac04..cc78789145 100644 --- a/interface/src/workload/PhysicsBoundary.cpp +++ b/interface/src/workload/PhysicsBoundary.cpp @@ -10,9 +10,13 @@ #include "PhysicsBoundary.h" +#include +#include #include #include +#include "avatar/AvatarManager.h" +#include "avatar/OtherAvatar.h" #include "workload/GameWorkload.h" void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const Inputs& inputs) { @@ -21,13 +25,27 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const return; } GameWorkloadContext* gameContext = static_cast(context.get()); - PhysicalEntitySimulationPointer simulation = gameContext->_simulation; const auto& regionChanges = inputs.get0(); for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) { const workload::Space::Change& change = regionChanges[i]; - auto entity = space->getOwner(change.proxyId).get(); - if (entity) { - simulation->changeEntity(entity); + auto nestable = space->getOwner(change.proxyId).get(); + if (nestable) { + switch (nestable->getNestableType()) { + case NestableType::Entity: { + gameContext->_simulation->changeEntity(std::static_pointer_cast(nestable)); + } + break; + case NestableType::Avatar: { + auto avatar = std::static_pointer_cast(nestable); + avatar->setWorkloadRegion(change.region); + if (avatar->isInPhysicsSimulation() != avatar->shouldBeInPhysicsSimulation()) { + DependencyManager::get()->queuePhysicsChange(avatar); + } + } + break; + default: + break; + } } } } diff --git a/interface/src/workload/PhysicsBoundary.h b/interface/src/workload/PhysicsBoundary.h index c316fa5686..bc1e851285 100644 --- a/interface/src/workload/PhysicsBoundary.h +++ b/interface/src/workload/PhysicsBoundary.h @@ -7,25 +7,22 @@ // 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_PhysicsGatekeeper_h -#define hifi_PhysicsGatekeeper_h +#ifndef hifi_PhysicsBoundary_h +#define hifi_PhysicsBoundary_h -#include #include #include -#include "PhysicalEntitySimulation.h" - class PhysicsBoundary { public: using Config = workload::Job::Config; using Inputs = workload::RegionTracker::Outputs; using Outputs = bool; - using JobModel = workload::Job::ModelI; // this doesn't work + using JobModel = workload::Job::ModelI; PhysicsBoundary() {} void configure(const Config& config) { } void run(const workload::WorkloadContextPointer& context, const Inputs& inputs); }; -#endif // hifi_PhysicsGatekeeper_h +#endif // hifi_PhysicsBoundary_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 7dbee0e669..a9af3b7725 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1483,9 +1483,6 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, getWorldPosition()) > MOVE_DISTANCE_THRESHOLD; - if (_moving) { - addPhysicsFlags(Simulation::DIRTY_POSITION); - } if (_moving || _hasNewJointData) { locationChanged(); } @@ -1627,20 +1624,6 @@ float Avatar::computeMass() { return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f); } -void Avatar::rebuildCollisionShape() { - addPhysicsFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); -} - -void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) { - _physicsCallback = cb; -} - -void Avatar::addPhysicsFlags(uint32_t flags) { - if (_physicsCallback) { - _physicsCallback(flags); - } -} - // thread-safe glm::vec3 Avatar::getLeftPalmPosition() const { return _leftPalmPositionCache.get(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6e8ac59f1..7d3ec39c4f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -50,8 +50,6 @@ enum ScreenTintLayer { class Texture; -using AvatarPhysicsCallback = std::function; - class Avatar : public AvatarData, public scriptable::ModelProvider { Q_OBJECT @@ -244,7 +242,7 @@ public: // (otherwise floating point error will cause problems at large positions). void applyPositionDelta(const glm::vec3& delta); - virtual void rebuildCollisionShape(); + virtual void rebuildCollisionShape() = 0; virtual void computeShapeInfo(ShapeInfo& shapeInfo); void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); @@ -332,10 +330,6 @@ public: render::ItemID getRenderItemID() { return _renderItemID; } bool isMoving() const { return _moving; } - void setPhysicsCallback(AvatarPhysicsCallback cb); - void addPhysicsFlags(uint32_t flags); - bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } - void fadeIn(render::ScenePointer scene); void fadeOut(render::ScenePointer scene, KillAvatarReason reason); bool isFading() const { return _isFading; } @@ -530,8 +524,6 @@ protected: int _voiceSphereID; - AvatarPhysicsCallback _physicsCallback { nullptr }; - float _displayNameTargetAlpha { 1.0f }; float _displayNameAlpha { 1.0f }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 34d8dbbaef..c3c4095251 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -295,7 +295,8 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r auto spaceIndex = _space->allocateID(); workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius()); workload::Transaction transaction; - transaction.reset(spaceIndex, sphere, workload::Owner(entity)); + SpatiallyNestablePointer nestable = std::static_pointer_cast(entity); + transaction.reset(spaceIndex, sphere, workload::Owner(nestable)); _space->enqueueTransaction(transaction); entity->setSpaceIndex(spaceIndex); connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c5035431f6..9430e97e54 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -64,6 +64,13 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _hazeIndex = INVALID_INDEX; } } + + if (_bloomStage) { + if (!BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomStage->removeBloom(_bloomIndex); + _bloomIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -112,6 +119,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_hazeStage); } + if (!_bloomStage) { + _bloomStage = args->_scene->getStage(); + assert(_bloomStage); + } + { // Sun // Need an update ? if (_needSunUpdate) { @@ -161,6 +173,15 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needBloomUpdate) { + if (BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomIndex = _bloomStage->addBloom(_bloom); + } + _needBloomUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -190,6 +211,12 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } + + if (_bloomMode == COMPONENT_MODE_DISABLED) { + _bloomStage->_currentFrame.pushBloom(INVALID_INDEX); + } else if (_bloomMode == COMPONENT_MODE_ENABLED) { + _bloomStage->_currentFrame.pushBloom(_bloomIndex); + } } } @@ -211,6 +238,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen bool ambientLightChanged = entity->ambientLightPropertiesChanged(); bool skyboxChanged = entity->skyboxPropertiesChanged(); bool hazeChanged = entity->hazePropertiesChanged(); + bool bloomChanged = entity->bloomPropertiesChanged(); entity->resetRenderingPropertiesChanged(); _lastPosition = entity->getWorldPosition(); @@ -221,6 +249,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _ambientLightProperties = entity->getAmbientLightProperties(); _skyboxProperties = entity->getSkyboxProperties(); _hazeProperties = entity->getHazeProperties(); + _bloomProperties = entity->getBloomProperties(); #if 0 if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { @@ -258,6 +287,10 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen if (hazeChanged) { updateHazeFromEntity(entity); } + + if (bloomChanged) { + updateBloomFromEntity(entity); + } } void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { @@ -276,6 +309,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || + entity->bloomPropertiesChanged() || entity->skyboxPropertiesChanged()) { return true; @@ -388,6 +422,16 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setTransform(entity->getTransform().getMatrix()); } +void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) { + setBloomMode((ComponentMode)entity->getBloomMode()); + + const auto& bloom = editBloom(); + + bloom->setBloomIntensity(_bloomProperties.getBloomIntensity()); + bloom->setBloomThreshold(_bloomProperties.getBloomThreshold()); + bloom->setBloomSize(_bloomProperties.getBloomSize()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { setSkyboxMode((ComponentMode)entity->getSkyboxMode()); @@ -510,6 +554,10 @@ void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { _skyboxMode = mode; } +void ZoneEntityRenderer::setBloomMode(ComponentMode mode) { + _bloomMode = mode; +} + void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index c48679e5d4..3e2690e1bd 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -1,7 +1,6 @@ // // RenderableZoneEntityItem.h // -// // Created by Clement on 4/22/15. // Copyright 2015 High Fidelity, Inc. // @@ -15,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "RenderableEntityItem.h" #include @@ -50,6 +51,7 @@ private: void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); + void updateBloomFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -59,6 +61,7 @@ private: void setKeyLightMode(ComponentMode mode); void setAmbientLightMode(ComponentMode mode); void setSkyboxMode(ComponentMode mode); + void setBloomMode(ComponentMode mode); void setSkyboxColor(const glm::vec3& color); void setProceduralUserData(const QString& userData); @@ -68,6 +71,7 @@ private: graphics::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } + graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -82,36 +86,43 @@ private: #endif LightStagePointer _stage; - const graphics::LightPointer _sunLight{ std::make_shared() }; - const graphics::LightPointer _ambientLight{ std::make_shared() }; - const graphics::SunSkyStagePointer _background{ std::make_shared() }; - const graphics::HazePointer _haze{ std::make_shared() }; + const graphics::LightPointer _sunLight { std::make_shared() }; + const graphics::LightPointer _ambientLight { std::make_shared() }; + const graphics::SunSkyStagePointer _background { std::make_shared() }; + const graphics::HazePointer _haze { std::make_shared() }; + const graphics::BloomPointer _bloom { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; + ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; - indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _ambientIndex{ LightStage::INVALID_INDEX }; + indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _shadowIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; BackgroundStagePointer _backgroundStage; - BackgroundStage::Index _backgroundIndex{ BackgroundStage::INVALID_INDEX }; + BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX }; HazeStagePointer _hazeStage; - HazeStage::Index _hazeIndex{ HazeStage::INVALID_INDEX }; + HazeStage::Index _hazeIndex { HazeStage::INVALID_INDEX }; + + BloomStagePointer _bloomStage; + BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; bool _needUpdate{ true }; bool _needSunUpdate{ true }; bool _needAmbientUpdate{ true }; bool _needBackgroundUpdate{ true }; bool _needHazeUpdate{ true }; + bool _needBloomUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; + BloomPropertyGroup _bloomProperties; // More attributes used for rendering: QString _ambientTextureURL; diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp new file mode 100644 index 0000000000..2c4d46ab35 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -0,0 +1,159 @@ +// +// BloomPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); +} + +void BloomPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); +} + +void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(bloomIntensity); + COPY_PROPERTY_IF_CHANGED(bloomThreshold); + COPY_PROPERTY_IF_CHANGED(bloomSize); +} + +void BloomPropertyGroup::debugDump() const { + qCDebug(entities) << " BloomPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _bloomIntensity:" << _bloomIntensity; + qCDebug(entities) << " _bloomThreshold:" << _bloomThreshold; + qCDebug(entities) << " _bloomSize:" << _bloomSize; +} + +void BloomPropertyGroup::listChangedProperties(QList& out) { + if (bloomIntensityChanged()) { + out << "bloom-bloomIntensity"; + } + if (bloomThresholdChanged()) { + out << "bloom-bloomThreshold"; + } + if (bloomSizeChanged()) { + out << "bloom-bloomSize"; + } +} + +bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); + + return true; +} + +bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_INTENSITY, BloomIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_THRESHOLD, BloomThreshold); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_SIZE, BloomSize); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void BloomPropertyGroup::markAllChanged() { + _bloomIntensityChanged = true; + _bloomThresholdChanged = true; + _bloomSizeChanged = true; +} + +EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_BLOOM_INTENSITY, bloomIntensity); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_THRESHOLD, bloomThreshold); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_SIZE, bloomSize); + + return changedProperties; +} + +void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomIntensity, getBloomIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomThreshold, getBloomThreshold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomSize, getBloomSize); +} + +bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomIntensity, bloomIntensity, setBloomIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomThreshold, bloomThreshold, setBloomThreshold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomSize, bloomSize, setBloomSize); + + return somethingChanged; +} + +EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_BLOOM_INTENSITY; + requestedProperties += PROP_BLOOM_THRESHOLD; + requestedProperties += PROP_BLOOM_SIZE; + + return requestedProperties; +} + +void BloomPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); +} + +int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + return bytesRead; +} diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h new file mode 100644 index 0000000000..a1f9b6d748 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -0,0 +1,94 @@ +// +// BloomPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_BloomPropertyGroup_h +#define hifi_BloomPropertyGroup_h + +#include +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; + +static const float INITIAL_BLOOM_INTENSITY { 0.25f }; +static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; +static const float INITIAL_BLOOM_SIZE { 0.9f }; + +/**jsdoc + * Bloom is defined by the following properties. + * @typedef {object} Entities.Bloom + * + * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. + * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. + * @property {number} bloomSize=0.9 - The size of the bloom effect. + */ +class BloomPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const BloomPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY(PROP_BLOOM_INTENSITY, BloomIntensity, bloomIntensity, float, INITIAL_BLOOM_INTENSITY); + DEFINE_PROPERTY(PROP_BLOOM_THRESHOLD, BloomThreshold, bloomThreshold, float, INITIAL_BLOOM_THRESHOLD); + DEFINE_PROPERTY(PROP_BLOOM_SIZE, BloomSize, bloomSize, float, INITIAL_BLOOM_SIZE); + +}; + +#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 27fe55673f..dc29d4bb2a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -37,6 +37,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; +BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; @@ -84,6 +85,7 @@ void EntityItemProperties::debugDump() const { getHaze().debugDump(); getKeyLight().debugDump(); getAmbientLight().debugDump(); + getBloom().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -211,6 +213,10 @@ QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } +QString EntityItemProperties::getBloomModeAsString() const { + return getComponentModeAsString(_bloomMode); +} + QString EntityItemProperties::getComponentModeString(uint32_t mode) { // return "inherit" if mode is not valid if (mode < COMPONENT_MODE_ITEM_COUNT) { @@ -235,6 +241,15 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { } } +void EntityItemProperties::setBloomModeFromString(const QString& bloomMode) { + auto result = findComponent(bloomMode); + + if (result != COMPONENT_MODES.end()) { + _bloomMode = result->first; + _bloomModeChanged = true; + } +} + QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } @@ -394,6 +409,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -454,6 +470,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _ambientLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); + changedProperties += _bloom.getChangedProperties(); return changedProperties; } @@ -1164,6 +1181,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * "enabled": The haze properties of this zone are enabled, overriding the haze from any enclosing zone. * @property {Entities.Haze} haze - The haze properties of the zone. * + * @property {string} bloomMode="inherit" - Configures the bloom in the zone. Possible values:
+ * "inherit": The bloom from any enclosing zone continues into this zone.
+ * "disabled": The bloom from any enclosing zone and the bloom of this zone are disabled in this zone.
+ * "enabled": The bloom properties of this zone are enabled, overriding the bloom from any enclosing zone. + * @property {Entities.Bloom} bloom - The bloom properties of the zone. + * * @property {boolean} flyingAllowed=true - If true then visitors can fly in the zone; otherwise they cannot. * @property {boolean} ghostingAllowed=true - If true then visitors with avatar collisions turned off will not * collide with content in the zone; otherwise visitors will always collide with content in the zone. @@ -1382,6 +1405,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); @@ -1630,6 +1656,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -1662,6 +1689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _ambientLight.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); + _bloom.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1803,6 +1831,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(keyLightMode); COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); + COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -1825,6 +1854,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _ambientLight.merge(other._ambientLight); _skybox.merge(other._skybox); _haze.merge(other._haze); + _bloom.merge(other._bloom); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -2096,6 +2126,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); + ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); + ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); @@ -2357,6 +2392,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticHaze.setProperties(properties); _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); + _staticBloom.setProperties(properties); + _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); @@ -2731,6 +2770,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); + properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); @@ -3044,10 +3086,12 @@ void EntityItemProperties::markAllChanged() { _skyboxModeChanged = true; _ambientLightModeChanged = true; _hazeModeChanged = true; + _bloomModeChanged = true; _animation.markAllChanged(); _skybox.markAllChanged(); _haze.markAllChanged(); + _bloom.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3442,15 +3486,15 @@ QList EntityItemProperties::listChangedProperties() { if (hazeModeChanged()) { out += "hazeMode"; } - + if (bloomModeChanged()) { + out += "bloomMode"; + } if (keyLightModeChanged()) { out += "keyLightMode"; } - if (ambientLightModeChanged()) { out += "ambientLightMode"; } - if (skyboxModeChanged()) { out += "skyboxMode"; } @@ -3581,6 +3625,7 @@ QList EntityItemProperties::listChangedProperties() { getAmbientLight().listChangedProperties(out); getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); + getBloom().listChangedProperties(out); return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 04e54c54a5..50305345de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -42,6 +42,7 @@ #include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" @@ -195,9 +196,11 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); + DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); @@ -533,6 +536,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BloomMode, bloomMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 156c5d9dd4..3932730661 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -257,6 +257,11 @@ enum EntityPropertyList { PROP_SPIN_SPREAD, PROP_PARTICLE_ROTATE_WITH_ENTITY, + PROP_BLOOM_MODE, + PROP_BLOOM_INTENSITY, + PROP_BLOOM_THRESHOLD, + PROP_BLOOM_SIZE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index f2550e5d3c..a7dfa1a41e 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -47,33 +47,27 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - // Contains a QString property, must be synchronized + // Contain QString properties, must be synchronized withReadLock([&] { _keyLightProperties.getProperties(properties); - }); - - withReadLock([&] { _ambientLightProperties.getProperties(properties); + _skyboxProperties.getProperties(properties); }); + _hazeProperties.getProperties(properties); + _bloomProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - // Contains a QString property, must be synchronized - withReadLock([&] { - _skyboxProperties.getProperties(properties); - }); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); - _hazeProperties.getProperties(properties); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); return properties; } @@ -102,32 +96,27 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie // Contains a QString property, must be synchronized withWriteLock([&] { _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties); - }); - withWriteLock([&] { _ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties); + _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); }); + _hazePropertiesChanged = _hazeProperties.setProperties(properties); + _bloomPropertiesChanged = _bloomProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - // Contains a QString property, must be synchronized - withWriteLock([&] { - _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); - _hazePropertiesChanged = _hazeProperties.setProperties(properties); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || - _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; + _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; return somethingChanged; } @@ -139,54 +128,67 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - int bytesFromKeylight; - withWriteLock([&] { - bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); - }); + { + int bytesFromKeylight; + withWriteLock([&] { + bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _keyLightPropertiesChanged; + bytesRead += bytesFromKeylight; + dataAt += bytesFromKeylight; + } - somethingChanged = somethingChanged || _keyLightPropertiesChanged; - bytesRead += bytesFromKeylight; - dataAt += bytesFromKeylight; + { + int bytesFromAmbientlight; + withWriteLock([&] { + bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _ambientLightPropertiesChanged; + bytesRead += bytesFromAmbientlight; + dataAt += bytesFromAmbientlight; + } - int bytesFromAmbientlight; - withWriteLock([&] { - bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); - }); + { + int bytesFromSkybox; + withWriteLock([&] { + bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); + }); + somethingChanged = somethingChanged || _skyboxPropertiesChanged; + bytesRead += bytesFromSkybox; + dataAt += bytesFromSkybox; + } - somethingChanged = somethingChanged || _ambientLightPropertiesChanged; - bytesRead += bytesFromAmbientlight; - dataAt += bytesFromAmbientlight; + { + int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _hazePropertiesChanged); + somethingChanged = somethingChanged || _hazePropertiesChanged; + bytesRead += bytesFromHaze; + dataAt += bytesFromHaze; + } + + { + int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _bloomPropertiesChanged); + somethingChanged = somethingChanged || _bloomPropertiesChanged; + bytesRead += bytesFromBloom; + dataAt += bytesFromBloom; + } READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - int bytesFromSkybox; - withWriteLock([&] { - bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); - }); - somethingChanged = somethingChanged || _skyboxPropertiesChanged; - bytesRead += bytesFromSkybox; - dataAt += bytesFromSkybox; - READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); - - int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _hazePropertiesChanged); - - somethingChanged = somethingChanged || _hazePropertiesChanged; - bytesRead += bytesFromHaze; - dataAt += bytesFromHaze; - READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); return bytesRead; } @@ -196,29 +198,24 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p withReadLock([&] { requestedProperties += _keyLightProperties.getEntityProperties(params); - }); - - withReadLock([&] { requestedProperties += _ambientLightProperties.getEntityProperties(params); + requestedProperties += _skyboxProperties.getEntityProperties(params); }); + requestedProperties += _hazeProperties.getEntityProperties(params); + requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; - withReadLock([&] { - requestedProperties += _skyboxProperties.getEntityProperties(params); - }); - requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; requestedProperties += PROP_HAZE_MODE; - requestedProperties += _hazeProperties.getEntityProperties(params); - requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; requestedProperties += PROP_SKYBOX_MODE; + requestedProperties += PROP_BLOOM_MODE; return requestedProperties; } @@ -235,44 +232,46 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeString(_bloomMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); _skyboxProperties.debugDump(); _hazeProperties.debugDump(); + _bloomProperties.debugDump(); } ShapeType ZoneEntityItem::getShapeType() const { @@ -344,6 +343,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _ambientLightPropertiesChanged = false; _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; + _bloomPropertiesChanged = false; _stagePropertiesChanged = false; }); } @@ -359,6 +359,17 @@ uint32_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getBloomMode() const { + return _bloomMode; +} + void ZoneEntityItem::setKeyLightMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { _keyLightMode = value; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 0aaa32a57a..e2ebf16f11 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -18,6 +18,7 @@ #include "EntityTree.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include class ZoneEntityItem : public EntityItem { @@ -79,9 +80,13 @@ public: void setSkyboxMode(uint32_t value); uint32_t getSkyboxMode() const; + void setBloomMode(const uint32_t value); + uint32_t getBloomMode() const; + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } @@ -93,10 +98,8 @@ public: bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } - - bool hazePropertiesChanged() const { - return _hazePropertiesChanged; - } + bool hazePropertiesChanged() const { return _hazePropertiesChanged; } + bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } bool stagePropertiesChanged() const { return _stagePropertiesChanged; } @@ -133,9 +136,11 @@ protected: uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; + uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; + BloomPropertyGroup _bloomProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -146,6 +151,7 @@ protected: bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged{ false }; + bool _bloomPropertiesChanged { false }; bool _stagePropertiesChanged { false }; static bool _drawZoneBoundaries; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fc94236c96..7bd87a4554 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -28,6 +28,20 @@ #include #include +#if defined(Q_OS_ANDROID) +#define FBX_PACK_NORMALS 0 +#else +#define FBX_PACK_NORMALS 1 +#endif + +#if FBX_PACK_NORMALS +using NormalType = glm::uint32; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 +#else +using NormalType = glm::vec3; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ +#endif + // See comment in FBXReader::parseFBX(). static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; static const QByteArray FBX_BINARY_PROLOG("Kaydara FBX Binary "); @@ -226,6 +240,7 @@ public: QVector vertices; QVector normals; QVector tangents; + mutable QVector normalsAndTangents; // Populated later if needed for blendshapes QVector colors; QVector texCoords; QVector texCoords1; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index ad002b237c..c391ea6647 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -34,20 +34,6 @@ class QIODevice; class FBXNode; -#if defined(Q_OS_ANDROID) -#define FBX_PACK_NORMALS 0 -#else -#define FBX_PACK_NORMALS 1 -#endif - -#if FBX_PACK_NORMALS -using NormalType = glm::uint32; -#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 -#else -using NormalType = glm::vec3; -#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ -#endif - /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); diff --git a/libraries/graphics/src/graphics/Bloom.cpp b/libraries/graphics/src/graphics/Bloom.cpp new file mode 100644 index 0000000000..f8dcda3292 --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.cpp @@ -0,0 +1,18 @@ +// +// Bloom.cpp +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "Bloom.h" + +using namespace graphics; + +const float Bloom::INITIAL_BLOOM_INTENSITY { 0.25f }; +const float Bloom::INITIAL_BLOOM_THRESHOLD { 0.7f }; +const float Bloom::INITIAL_BLOOM_SIZE { 0.9f }; \ No newline at end of file diff --git a/libraries/graphics/src/graphics/Bloom.h b/libraries/graphics/src/graphics/Bloom.h new file mode 100644 index 0000000000..6ab48a35a3 --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.h @@ -0,0 +1,41 @@ +// +// Bloom.h +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_model_Bloom_h +#define hifi_model_Bloom_h + +#include + +namespace graphics { + class Bloom { + public: + // Initial values + static const float INITIAL_BLOOM_INTENSITY; + static const float INITIAL_BLOOM_THRESHOLD; + static const float INITIAL_BLOOM_SIZE; + + Bloom() {} + + void setBloomIntensity(const float bloomIntensity) { _bloomIntensity = bloomIntensity; } + void setBloomThreshold(const float bloomThreshold) { _bloomThreshold = bloomThreshold; } + void setBloomSize(const float bloomSize) { _bloomSize = bloomSize; } + + float getBloomIntensity() { return _bloomIntensity; } + float getBloomThreshold() { return _bloomThreshold; } + float getBloomSize() { return _bloomSize; } + + private: + float _bloomIntensity { INITIAL_BLOOM_INTENSITY }; + float _bloomThreshold {INITIAL_BLOOM_THRESHOLD }; + float _bloomSize { INITIAL_BLOOM_SIZE }; + }; + using BloomPointer = std::shared_ptr; +} +#endif // hifi_model_Bloom_h diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 5c7e6ff892..2fd0d90da3 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -20,9 +20,6 @@ #include #include -#include -#include - namespace glm { using hvec2 = glm::tvec2; using hvec4 = glm::tvec4; @@ -62,32 +59,6 @@ namespace { } } -void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { - auto absNormal = glm::abs(normal); - auto absTangent = glm::abs(tangent); - normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); - tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); - normal = glm::clamp(normal, -1.0f, 1.0f); - tangent = glm::clamp(tangent, -1.0f, 1.0f); - normal *= 511.0f; - tangent *= 511.0f; - normal = glm::round(normal); - tangent = glm::round(tangent); - - glm::detail::i10i10i10i2 normalStruct; - glm::detail::i10i10i10i2 tangentStruct; - normalStruct.data.x = int(normal.x); - normalStruct.data.y = int(normal.y); - normalStruct.data.z = int(normal.z); - normalStruct.data.w = 0; - tangentStruct.data.x = int(tangent.x); - tangentStruct.data.y = int(tangent.y); - tangentStruct.data.z = int(tangent.z); - tangentStruct.data.w = 0; - packedNormal = normalStruct.pack; - packedTangent = tangentStruct.pack; -} - template glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function func) { QVector result; diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index f877341d50..a9707c3128 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "GpuHelpers.h" @@ -44,7 +46,31 @@ namespace buffer_helpers { gpu::BufferView clone(const gpu::BufferView& input); gpu::BufferView resized(const gpu::BufferView& input, glm::uint32 numElements); - void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent); + inline void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { + auto absNormal = glm::abs(normal); + auto absTangent = glm::abs(tangent); + normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); + tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); + normal = glm::clamp(normal, -1.0f, 1.0f); + tangent = glm::clamp(tangent, -1.0f, 1.0f); + normal *= 511.0f; + tangent *= 511.0f; + normal = glm::round(normal); + tangent = glm::round(tangent); + + glm::detail::i10i10i10i2 normalStruct; + glm::detail::i10i10i10i2 tangentStruct; + normalStruct.data.x = int(normal.x); + normalStruct.data.y = int(normal.y); + normalStruct.data.z = int(normal.z); + normalStruct.data.w = 0; + tangentStruct.data.x = int(tangent.x); + tangentStruct.data.y = int(tangent.y); + tangentStruct.data.z = int(tangent.z); + tangentStruct.data.w = 0; + packedNormal = normalStruct.pack; + packedTangent = tangentStruct.pack; + } namespace mesh { glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function func); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dd351ef940..e458ffab7e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -273,6 +273,7 @@ void NodeList::reset(bool skipDomainHandlerReset) { // refresh the owner UUID to the NULL UUID setSessionUUID(QUuid()); + setSessionLocalID(Node::NULL_LOCAL_ID); // if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking if (_dtlsSocket) { @@ -647,6 +648,23 @@ void NodeList::processDomainServerList(QSharedPointer message) Node::LocalID newLocalID; packetStream >> newUUID; packetStream >> newLocalID; + + // when connected, if the session ID or local ID were not null and changed, we should reset + auto currentLocalID = getSessionLocalID(); + auto currentSessionID = getSessionUUID(); + if (_domainHandler.isConnected() && + ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || + (!currentSessionID.isNull() && newUUID != currentSessionID))) { + qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; + + // reset the nodelist, but don't do a domain handler reset since we're about to process a good domain list + reset(true); + + // tell the domain handler that we're no longer connected so that below + // it can re-perform actions as if we just connected + _domainHandler.setIsConnected(false); + } + setSessionLocalID(newLocalID); setSessionUUID(newUUID); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 566850b30d..8cb7c9aaa4 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::ParticleSpin); + return static_cast(EntityVersion::BloomEffect); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 8a2add3bb3..84a93ac939 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -241,7 +241,8 @@ enum class EntityVersion : PacketVersion { CollisionMask16Bytes, YieldSimulationOwnership, ParticleEntityFix, - ParticleSpin + ParticleSpin, + BloomEffect }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index c6474c1fe4..3d387e0956 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -119,7 +119,7 @@ bool Octree::recurseElementWithOperationSorted(const OctreeElementPointer& eleme } if (sortedChildren.size() > 1) { - static auto comparator = [](const SortedChild& left, const SortedChild& right) { return left.first > right.first; }; + static auto comparator = [](const SortedChild& left, const SortedChild& right) { return left.first < right.first; }; std::sort(sortedChildren.begin(), sortedChildren.end(), comparator); } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 163005bc81..d5ce60bef9 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -77,6 +77,10 @@ uint32_t PhysicsEngine::getNumSubsteps() const { return _dynamicsWorld->getNumSubsteps(); } +int32_t PhysicsEngine::getNumCollisionObjects() const { + return _dynamicsWorld ? _dynamicsWorld->getNumCollisionObjects() : 0; +} + // private void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { assert(motionState); @@ -279,6 +283,57 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { } } +void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) { + // removes + for (auto object : transaction.objectsToRemove) { + btRigidBody* body = object->getRigidBody(); + if (body) { + removeDynamicsForBody(body); + _dynamicsWorld->removeRigidBody(body); + + // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. + object->setRigidBody(nullptr); + body->setMotionState(nullptr); + delete body; + } + object->clearIncomingDirtyFlags(); + } + + // adds + for (auto object : transaction.objectsToAdd) { + addObjectToDynamicsWorld(object); + } + + // changes + std::vector failedChanges; + for (auto object : transaction.objectsToChange) { + uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + if (flags & HARD_DIRTY_PHYSICS_FLAGS) { + if (object->handleHardAndEasyChanges(flags, this)) { + object->clearIncomingDirtyFlags(); + } else { + failedChanges.push_back(object); + } + } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(flags); + object->clearIncomingDirtyFlags(); + } + if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { + _activeStaticBodies.insert(object->getRigidBody()); + } + } + // activeStaticBodies have changed (in an Easy way) and need their Aabbs updated + // but we've configured Bullet to NOT update them automatically (for improved performance) + // so we must do it ourselves + std::set::const_iterator itr = _activeStaticBodies.begin(); + while (itr != _activeStaticBodies.end()) { + _dynamicsWorld->updateSingleAabb(*itr); + ++itr; + } + // we replace objectsToChange with any that failed + transaction.objectsToChange.swap(failedChanges); +} + void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { // trigger events for new/existing/old contacts ContactMap::iterator contactItr = _contactMap.begin(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index c6e165632b..317f7ef312 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -70,11 +70,24 @@ using CollisionEvents = std::vector; class PhysicsEngine { public: + class Transaction { + public: + void clear() { + objectsToRemove.clear(); + objectsToAdd.clear(); + objectsToChange.clear(); + } + std::vector objectsToRemove; + std::vector objectsToAdd; + std::vector objectsToChange; + }; + PhysicsEngine(const glm::vec3& offset); ~PhysicsEngine(); void init(); uint32_t getNumSubsteps() const; + int32_t getNumCollisionObjects() const; void removeObjects(const VectorOfMotionStates& objects); void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown @@ -83,6 +96,8 @@ public: VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); + void processTransaction(Transaction& transaction); + void stepSimulation(); void harvestPerformanceStats(); void printPerformanceStatsToFile(const QString& filename); @@ -160,7 +175,7 @@ private: CharacterController* _myAvatarController; - uint32_t _numContactFrames = 0; + uint32_t _numContactFrames { 0 }; bool _dumpNextStats { false }; bool _saveNextStats { false }; diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 2a9a9b3850..3a35e736a9 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -25,11 +25,7 @@ BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) { _parameters.edit()._sampleCount = downsamplingFactor; } -void BloomThreshold::configure(const Config& config) { - if (_parameters.get()._threshold != config.threshold) { - _parameters.edit()._threshold = config.threshold; - } -} +void BloomThreshold::configure(const Config& config) {} void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); @@ -39,6 +35,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons const auto frameTransform = inputs.get0(); const auto inputFrameBuffer = inputs.get1(); + const auto bloom = inputs.get2(); assert(inputFrameBuffer->hasColor()); @@ -68,6 +65,13 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + if (!bloom) { + renderContext->taskFlow.abortTask(); + return; + } + + _parameters.edit()._threshold = bloom->getBloomThreshold(); + gpu::doInBatch("BloomThreshold::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -83,23 +87,15 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons batch.draw(gpu::TRIANGLE_STRIP, 4); }); - outputs = _outputBuffer; + outputs.edit0() = _outputBuffer; + outputs.edit1() = 0.5f + bloom->getBloomSize() * 3.5f; } BloomApply::BloomApply() { } -void BloomApply::configure(const Config& config) { - const auto newIntensity = config.intensity / 3.0f; - - if (_parameters.get()._intensities.x != newIntensity) { - auto& parameters = _parameters.edit(); - parameters._intensities.x = newIntensity; - parameters._intensities.y = newIntensity; - parameters._intensities.z = newIntensity; - } -} +void BloomApply::configure(const Config& config) {} void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); @@ -109,7 +105,6 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In static const auto BLUR0_SLOT = 0; static const auto BLUR1_SLOT = 1; static const auto BLUR2_SLOT = 2; - static const auto PARAMETERS_SLOT = 0; if (!_pipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::bloomApply); @@ -123,8 +118,15 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In const auto blur0FB = inputs.get1(); const auto blur1FB = inputs.get2(); const auto blur2FB = inputs.get3(); + const auto bloom = inputs.get4(); const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y }; + const auto newIntensity = bloom->getBloomIntensity() / 3.0f; + auto& parameters = _parameters.edit(); + parameters._intensities.x = newIntensity; + parameters._intensities.y = newIntensity; + parameters._intensities.z = newIntensity; + gpu::doInBatch("BloomApply::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -139,7 +141,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0)); batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0)); batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0)); - batch.setUniformBuffer(PARAMETERS_SLOT, _parameters); + batch.setUniformBuffer(render_utils::slot::buffer::BloomParams, _parameters); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } @@ -206,8 +208,6 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In level2FB->getRenderBuffer(0) }; - static auto TEXCOORD_RECT_SLOT = 1; - if (!_pipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::drawTextureOpaqueTexcoordRect); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -227,7 +227,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In Transform modelTransform; if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) { - batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f); + batch._glUniform4f(gpu::slot::uniform::TexCoordRect, 0.0f, 0.0f, 1.f, 1.f); modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2); modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f)); @@ -255,7 +255,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In viewport.z /= 2; - batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f); + batch._glUniform4f(gpu::slot::uniform::TexCoordRect, 0.5f, 0.0f, 0.5f, 1.f); modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport); modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f)); @@ -266,44 +266,10 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In }); } -void BloomConfig::setIntensity(float value) { - auto task = static_cast(_task); - auto blurJobIt = task->editJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("intensity", value); -} +BloomEffect::BloomEffect() {} -float BloomConfig::getIntensity() const { - auto task = static_cast(_task); - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - return blurJobIt->getConfiguration()->property("intensity").toFloat(); -} - -void BloomConfig::setSize(float value) { - std::string blurName{ "BloomBlurN" }; - auto sigma = 0.5f+value*3.5f; - auto task = static_cast(_task); - - for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { - blurName.back() = '0' + i; - auto blurJobIt = task->editJob(blurName); - assert(blurJobIt != task->_jobs.end()); - auto& gaussianBlur = blurJobIt->edit(); - auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setFilterGaussianTaps(9, sigma); - } - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("sigma", sigma); -} - -Bloom::Bloom() { - -} - -void Bloom::configure(const Config& config) { - std::string blurName{ "BloomBlurN" }; +void BloomEffect::configure(const Config& config) { + std::string blurName { "BloomBlurN" }; for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { blurName.back() = '0' + i; @@ -312,25 +278,30 @@ void Bloom::configure(const Config& config) { } } -void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { +void BloomEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { // Start by computing threshold of color buffer input at quarter resolution - const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U); + const auto bloomOutputs = task.addJob("BloomThreshold", inputs, 4U); // Multi-scale blur, each new blur is half resolution of the previous pass - const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer, true); - const auto blurFB1 = task.addJob("BloomBlur1", blurFB0, true, 2U); - const auto blurFB2 = task.addJob("BloomBlur2", blurFB1, true, 2U); + const auto blurInputBuffer = bloomOutputs.getN(0); + const auto sigma = bloomOutputs.getN(1); + const auto blurInput0 = render::BlurGaussian::Inputs(blurInputBuffer, true, 1U, 9, sigma).asVarying(); + const auto blurFB0 = task.addJob("BloomBlur0", blurInput0); + const auto blurInput1 = render::BlurGaussian::Inputs(blurFB0, true, 2U, 9, sigma).asVarying(); + const auto blurFB1 = task.addJob("BloomBlur1", blurInput1); + const auto blurInput2 = render::BlurGaussian::Inputs(blurFB1, true, 2U, 9, sigma).asVarying(); + const auto blurFB2 = task.addJob("BloomBlur2", blurInput2); - const auto& input = inputs.get(); - const auto& frameBuffer = input[1]; + const auto& frameBuffer = inputs.getN(1); + const auto& bloom = inputs.getN(2); // Mix all blur levels at quarter resolution - const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + const auto applyInput = BloomApply::Inputs(blurInputBuffer, blurFB0, blurFB1, blurFB2, bloom).asVarying(); task.addJob("BloomApply", applyInput); // And then blend result in additive manner on top of final color buffer - const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying(); + const auto drawInput = BloomDraw::Inputs(frameBuffer, blurInputBuffer).asVarying(); task.addJob("BloomDraw", drawInput); - const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying(); + const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, blurInputBuffer).asVarying(); task.addJob("DebugBloom", debugInput); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 04cb4a9474..7789c70190 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -14,43 +14,22 @@ #include +#include "graphics/Bloom.h" + #include "DeferredFrameTransform.h" class BloomConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) - Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) - -public: - - BloomConfig() : render::Task::Config(false) {} - - float size{ 0.7f }; - - void setIntensity(float value); - float getIntensity() const; - void setSize(float value); - -signals: - void dirty(); }; class BloomThresholdConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) - -public: - - float threshold{ 0.9f }; - -signals: - void dirty(); }; class BloomThreshold { public: - using Inputs = render::VaryingSet2; - using Outputs = gpu::FramebufferPointer; + using Inputs = render::VaryingSet3; + using Outputs = render::VaryingSet2; using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; @@ -71,21 +50,11 @@ private: class BloomApplyConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) - Q_PROPERTY(float sigma MEMBER sigma NOTIFY dirty) - -public: - - float intensity{ 0.25f }; - float sigma{ 1.0f }; - -signals: - void dirty(); }; class BloomApply { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = BloomApplyConfig; using JobModel = render::Job::ModelI; @@ -118,7 +87,7 @@ private: class DebugBloomConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) + Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) public: @@ -155,13 +124,13 @@ private: DebugBloomConfig::Mode _mode; }; -class Bloom { +class BloomEffect { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Config = BloomConfig; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelI; - Bloom(); + BloomEffect(); void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp new file mode 100644 index 0000000000..904e27f5da --- /dev/null +++ b/libraries/render-utils/src/BloomStage.cpp @@ -0,0 +1,79 @@ +// +// BloomStage.cpp +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomStage.h" + +#include "DeferredLightingEffect.h" + +#include + +std::string BloomStage::_stageName { "BLOOM_STAGE"}; +const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +FetchBloomStage::FetchBloomStage() { + _bloom = std::make_shared(); +} + +void FetchBloomStage::configure(const Config& config) { + _bloom->setBloomIntensity(config.bloomIntensity); + _bloom->setBloomThreshold(config.bloomThreshold); + _bloom->setBloomSize(config.bloomSize); +} + +BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { + auto found = _bloomMap.find(bloom); + if (found != _bloomMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +BloomStage::Index BloomStage::addBloom(const BloomPointer& bloom) { + auto found = _bloomMap.find(bloom); + if (found == _bloomMap.end()) { + auto bloomId = _blooms.newElement(bloom); + // Avoid failing to allocate a bloom, just pass + if (bloomId != INVALID_INDEX) { + // Insert the bloom and its index in the reverse map + _bloomMap.insert(BloomMap::value_type(bloom, bloomId)); + } + return bloomId; + } else { + return (*found).second; + } +} + +BloomStage::BloomPointer BloomStage::removeBloom(Index index) { + BloomPointer removed = _blooms.freeElement(index); + if (removed) { + _bloomMap.erase(removed); + } + return removed; +} + +BloomStageSetup::BloomStageSetup() {} + +void BloomStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(BloomStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(BloomStage::getName(), std::make_shared()); + } +} + +void FetchBloomStage::run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom) { + auto bloomStage = renderContext->_scene->getStage(); + assert(bloomStage); + + bloom = nullptr; + if (bloomStage->_currentFrame._blooms.size() != 0) { + auto bloomId = bloomStage->_currentFrame._blooms.front(); + bloom = bloomStage->getBloom(bloomId); + } +} diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h new file mode 100644 index 0000000000..27bd97dcab --- /dev/null +++ b/libraries/render-utils/src/BloomStage.h @@ -0,0 +1,118 @@ +// +// BloomStage.h + +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_BloomStage_h +#define hifi_render_utils_BloomStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Bloom stage to set up bloom-related rendering tasks +class BloomStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using BloomPointer = graphics::BloomPointer; + using Blooms = render::indexed_container::IndexedPointerVector; + using BloomMap = std::unordered_map; + + using BloomIndices = std::vector; + + Index findBloom(const BloomPointer& bloom) const; + Index addBloom(const BloomPointer& bloom); + + BloomPointer removeBloom(Index index); + + bool checkBloomId(Index index) const { return _blooms.checkIndex(index); } + + Index getNumBlooms() const { return _blooms.getNumElements(); } + Index getNumFreeBlooms() const { return _blooms.getNumFreeIndices(); } + Index getNumAllocatedBlooms() const { return _blooms.getNumAllocatedIndices(); } + + BloomPointer getBloom(Index bloomId) const { + return _blooms.get(bloomId); + } + + Blooms _blooms; + BloomMap _bloomMap; + + class Frame { + public: + Frame() {} + + void clear() { _blooms.clear(); } + + void pushBloom(BloomStage::Index index) { _blooms.emplace_back(index); } + + BloomStage::BloomIndices _blooms; + }; + + Frame _currentFrame; +}; +using BloomStagePointer = std::shared_ptr; + +class BloomStageSetup { +public: + using JobModel = render::Job::Model; + + BloomStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +class FetchBloomConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty); + Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty); + Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty); + +public: + FetchBloomConfig() : render::Job::Config() {} + + float bloomIntensity { graphics::Bloom::INITIAL_BLOOM_INTENSITY }; + float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD }; + float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE }; + +public slots: + void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); } + void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); } + void setBloomSize(const float value) { bloomSize = value; emit dirty(); } + +signals: + void dirty(); +}; + +class FetchBloomStage { +public: + using Config = FetchBloomConfig; + using JobModel = render::Job::ModelO; + + FetchBloomStage(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom); + +private: + graphics::BloomPointer _bloom; +}; +#endif diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..3966d04b6d 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -76,6 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -85,6 +86,10 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); auto material = getGeometry()->getShapeMaterial(shapeID); @@ -170,9 +175,10 @@ void CauterizedModel::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } } diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index 8f137cb280..b48168e376 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -163,6 +163,5 @@ public: private: graphics::HazePointer _haze; - gpu::PipelinePointer _hazePipeline; }; #endif diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ca6e736a65..94a5026fb4 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -208,7 +208,9 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); - _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; + if (!model->getFBXGeometry().meshes[meshIndex].blendshapes.isEmpty()) { + _blendedVertexBuffer = model->_blendedVertexBuffers[meshIndex]; + } auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 18ee05ddd4..d19328aa8a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -42,8 +42,9 @@ using namespace std; int nakedModelPointerTypeId = qRegisterMetaType(); -int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); -int vec3VectorTypeId = qRegisterMetaType >(); +int weakGeometryResourceBridgePointerTypeId = qRegisterMetaType(); +int vec3VectorTypeId = qRegisterMetaType>(); +int normalTypeVecTypeId = qRegisterMetaType>("QVector"); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" @@ -301,41 +302,52 @@ bool Model::updateGeometry() { assert(_meshStates.empty()); const FBXGeometry& fbxGeometry = getFBXGeometry(); + int i = 0; foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); - // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index - // later in ModelMeshPayload, however the vast majority of meshes will not have them. - // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? - auto buffer = std::make_shared(); if (!mesh.blendshapes.isEmpty()) { - std::vector normalsAndTangents; - normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size()); - - for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin(); - normalIt != mesh.normals.end(); - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - normalsAndTangents.push_back(finalNormal); - normalsAndTangents.push_back(finalTangent); + if (!_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); } + const auto& buffer = _blendedVertexBuffers[i]; + QVector normalsAndTangents; + normalsAndTangents.resize(2 * mesh.normals.size()); - buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + // Interleave normals and tangents + // Parallel version for performance + tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { + auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); + auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); + auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); + + for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; + normalIt != normalsRange.second; + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto& finalNormal = *normalIt; + const auto& finalTangent = *tangentIt; +#endif + *normalsAndTangentsIt = finalNormal; + ++normalsAndTangentsIt; + *normalsAndTangentsIt = finalTangent; + ++normalsAndTangentsIt; + } + }); + const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); + buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType)); + buffer->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); + buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + mesh.normalsAndTangents = normalsAndTangents; } - _blendedVertexBuffers.push_back(buffer); + i++; } needFullUpdate = true; emit rigReady(); @@ -432,7 +444,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } if (sortedTriangleSets.size() > 1) { - static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance < right.distance; }; std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); } @@ -575,7 +587,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co } if (sortedTriangleSets.size() > 1) { - static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance < right.distance; }; std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); } @@ -1327,20 +1339,21 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe void Blender::run() { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); - QVector vertices, normals, tangents; + QVector vertices; + QVector normalsAndTangents; if (_model) { int offset = 0; + int normalsAndTangentsOffset = 0; foreach (const FBXMesh& mesh, _meshes) { if (mesh.blendshapes.isEmpty()) { continue; } vertices += mesh.vertices; - normals += mesh.normals; - tangents += mesh.tangents; + normalsAndTangents += mesh.normalsAndTangents; glm::vec3* meshVertices = vertices.data() + offset; - glm::vec3* meshNormals = normals.data() + offset; - glm::vec3* meshTangents = tangents.data() + offset; + NormalType* meshNormalsAndTangents = normalsAndTangents.data() + normalsAndTangentsOffset; offset += mesh.vertices.size(); + normalsAndTangentsOffset += mesh.normalsAndTangents.size(); const float NORMAL_COEFFICIENT_SCALE = 0.01f; for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { float vertexCoefficient = _blendshapeCoefficients.at(i); @@ -1350,22 +1363,39 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; const FBXBlendshape& blendshape = mesh.blendshapes.at(i); - for (int j = 0; j < blendshape.indices.size(); j++) { - int index = blendshape.indices.at(j); - meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; - meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; - if (blendshape.tangents.size() > j) { - meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient; + tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { + for (auto j = range.begin(); j < range.end(); j++) { + int index = blendshape.indices.at(j); + meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; + + glm::vec3 normal = mesh.normals.at(index) + blendshape.normals.at(j) * normalCoefficient; + glm::vec3 tangent; + if (index < mesh.tangents.size()) { + tangent = mesh.tangents.at(index); + if ((int)j < blendshape.tangents.size()) { + tangent += blendshape.tangents.at(j) * normalCoefficient; + } + } +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + buffer_helpers::packNormalAndTangent(normal, tangent, finalNormal, finalTangent); +#else + const auto& finalNormal = normal; + const auto& finalTangent = tangent; +#endif + meshNormalsAndTangents[2 * index] = finalNormal; + meshNormalsAndTangents[2 * index + 1] = finalTangent; } - } + }); } } } - // post the result to the geometry cache, which will dispatch to the model if still alive + // post the result to the ModelBlender, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), - Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), - Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), + Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const QVector&, normalsAndTangents)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1525,9 +1555,10 @@ void Model::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } } @@ -1544,83 +1575,31 @@ bool Model::maybeStartBlender() { } void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents) { + const QVector& vertices, const QVector& normalsAndTangents) { auto geometryRef = geometry.lock(); - if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; - std::vector normalsAndTangents; + int normalAndTangentIndex = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { continue; } - gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; const auto vertexCount = mesh.vertices.size(); const auto verticesSize = vertexCount * sizeof(glm::vec3); - const auto offset = index * sizeof(glm::vec3); - - normalsAndTangents.clear(); - normalsAndTangents.resize(normals.size()+tangents.size()); -// assert(normalsAndTangents.size() == 2 * vertexCount); - - // Interleave normals and tangents -#if 0 - // Sequential version for debugging - auto normalsRange = std::make_pair(normals.begin() + index, normals.begin() + index + vertexCount); - auto tangentsRange = std::make_pair(tangents.begin() + index, tangents.begin() + index + vertexCount); - auto normalsAndTangentsIt = normalsAndTangents.begin(); - - for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; - normalIt != normalsRange.second; - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - *normalsAndTangentsIt = finalNormal; - ++normalsAndTangentsIt; - *normalsAndTangentsIt = finalTangent; - ++normalsAndTangentsIt; - } -#else - // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(index, index+vertexCount), [&](const tbb::blocked_range& range) { - auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end()); - auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end()); - auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2; - - for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; - normalIt != normalsRange.second; - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - *normalsAndTangentsIt = finalNormal; - ++normalsAndTangentsIt; - *normalsAndTangentsIt = finalTangent; - ++normalsAndTangentsIt; - } - }); -#endif - - buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + offset); - buffer->setSubData(verticesSize, 2 * vertexCount * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + const auto& buffer = _blendedVertexBuffers[i]; + assert(buffer); + buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType)); + buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3)); + buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); index += vertexCount; + normalAndTangentIndex += 2 * mesh.normals.size(); } } @@ -1686,6 +1665,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + auto fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1695,6 +1675,9 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { + _blendedVertexBuffers[i] = std::make_shared(); + } _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1807,11 +1790,10 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, - const QVector& tangents) { +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, + const QVector& vertices, const QVector& normalsAndTangents) { if (model) { - model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents); + model->setBlendedVertices(blendNumber, geometry, vertices, normalsAndTangents); } _pendingBlenders--; { @@ -1827,4 +1809,3 @@ void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const } } } - diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index adb2fcdd3d..9a7f9f3848 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -145,7 +145,7 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents); + const QVector& vertices, const QVector& normalsAndTangents); bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isAddedToScene() const { return _addedToScene; } @@ -423,7 +423,7 @@ protected: QUrl _url; - gpu::Buffers _blendedVertexBuffers; + std::unordered_map _blendedVertexBuffers; QVector > > _dilatedTextures; @@ -513,9 +513,12 @@ public: /// Adds the specified model to the list requiring vertex blends. void noteRequiresBlend(ModelPointer model); + bool shouldComputeBlendshapes() { return _computeBlendshapes; } + public slots: void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, const QVector& tangents); + const QVector& vertices, const QVector& normalsAndTangents); + void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; } private: using Mutex = std::mutex; @@ -527,6 +530,8 @@ private: std::set> _modelsRequiringBlends; int _pendingBlenders; Mutex _mutex; + + bool _computeBlendshapes { true }; }; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 9ea1669cc0..57f5c3ec34 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -45,6 +45,7 @@ #include "TextureCache.h" #include "ZoneRenderer.h" #include "FadeEffect.h" +#include "BloomStage.h" #include "RenderUtilsLogging.h" #include "AmbientOcclusionEffect.h" @@ -173,7 +174,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto velocityBufferOutputs = task.addJob("VelocityBuffer", velocityBufferInputs); const auto velocityBuffer = velocityBufferOutputs.getN(0); - // Clear Light, Haze and Skybox Stages and render zones from the general metas bucket + // Clear Light, Haze, Bloom, and Skybox Stages and render zones from the general metas bucket const auto zones = task.addJob("ZoneRenderer", metas); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. @@ -245,8 +246,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Antialiasing", antialiasingInputs); // Add bloom - const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying(); - task.addJob("Bloom", bloomInputs); + const auto bloomModel = task.addJob("BloomModel"); + const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomModel).asVarying(); + task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 079e6f75ef..f9b90f38ba 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -77,8 +77,9 @@ void SoftAttachmentModel::updateClusterMatrices() { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + auto modelBlender = DependencyManager::get(); + if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; - DependencyManager::get()->noteRequiresBlend(getThisPointer()); + modelBlender->noteRequiresBlend(getThisPointer()); } } diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index e05f28ef0d..be61953073 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -14,6 +14,7 @@ #include "LightStage.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "BloomStage.h" #include #include #include "DeferredLightingEffect.h" @@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("LightStageSetup"); task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); + task.addJob("BloomStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 3299b0c41c..1a1b3706f9 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -21,11 +21,12 @@ #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" - #include "render-utils/ShaderConstants.h" #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" +#include "BloomStage.h" + namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; @@ -63,7 +64,7 @@ void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& oupu } void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) { - // Grab light, background and haze stages and clear them + // Grab light, background, haze, and bloom stages and clear them auto lightStage = context->_scene->getStage(); assert(lightStage); lightStage->_currentFrame.clear(); @@ -76,6 +77,10 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) assert(hazeStage); hazeStage->_currentFrame.clear(); + auto bloomStage = context->_scene->getStage(); + assert(bloomStage); + bloomStage->_currentFrame.clear(); + // call render over the zones to grab their components in the correct order first... render::renderItems(context, inputs); @@ -84,6 +89,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight()); backgroundStage->_currentFrame.pushBackground(0); hazeStage->_currentFrame.pushHaze(0); + bloomStage->_currentFrame.pushBloom(INVALID_INDEX); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index b76793ea2d..edbfbdaff0 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -195,9 +195,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra return true; } -BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsampleFactor) : - _inOutResources(generateOutputFramebuffer, downsampleFactor) -{ +BlurGaussian::BlurGaussian() { _parameters = std::make_shared(); } @@ -243,12 +241,17 @@ void BlurGaussian::configure(const Config& config) { } -void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer) { +void BlurGaussian::run(const RenderContextPointer& renderContext, const Inputs& inputs, gpu::FramebufferPointer& blurredFramebuffer) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; + const auto sourceFramebuffer = inputs.get0(); + _inOutResources._generateOutputFramebuffer = inputs.get1(); + _inOutResources._downsampleFactor = inputs.get2(); + _parameters->setFilterGaussianTaps(inputs.get3(), inputs.get4()); + BlurInOutResource::Resources blurringResources; if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) { // early exit if no valid blurring resources diff --git a/libraries/render/src/render/BlurTask.h b/libraries/render/src/render/BlurTask.h index e8d268dc63..cb11570f5c 100644 --- a/libraries/render/src/render/BlurTask.h +++ b/libraries/render/src/render/BlurTask.h @@ -72,6 +72,7 @@ using BlurParamsPointer = std::shared_ptr; class BlurInOutResource { public: + BlurInOutResource() {} BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor); struct Resources { @@ -113,13 +114,14 @@ protected: class BlurGaussian { public: + using Inputs = VaryingSet5; using Config = BlurGaussianConfig; - using JobModel = Job::ModelIO; + using JobModel = Job::ModelIO; - BlurGaussian(bool generateOutputFramebuffer = false, unsigned int downsampleFactor = 1U); + BlurGaussian(); void configure(const Config& config); - void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, gpu::FramebufferPointer& blurredFramebuffer); BlurParamsPointer getParameters() const { return _parameters; } diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 854c4c3f36..3f2f7cd7fb 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -233,9 +233,8 @@ bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& directio tmax = glm::min(tmax, newTmax); } - bool inside = tmin < 0.0f; if (tmax >= glm::max(tmin, 0.0f)) { - if (inside) { + if (tmin < 0.0f) { distance = tmax; bool positiveDirection = direction[maxAxis] > 0.0f; surfaceNormal = glm::vec3(0.0f); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index ccb275ffc9..4b8768704a 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -22,8 +22,8 @@ const float defaultAACubeSize = 1.0f; const int MAX_PARENTING_CHAIN_SIZE = 30; SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : - _nestableType(nestableType), _id(id), + _nestableType(nestableType), _transform() { // set flags in _transform _transform.setTranslation(glm::vec3(0.0f)); diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 361f0aaf17..a50e687a9b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -212,7 +212,6 @@ public: virtual void parentDeleted() { } // called on children of a deleted parent protected: - const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -232,6 +231,8 @@ protected: quint64 _rotationChanged { 0 }; private: + SpatiallyNestable() = delete; + const NestableType _nestableType; // EntityItem or an AvatarData QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { INVALID_JOINT_INDEX }; // which joint of the parent is this relative to? diff --git a/scripts/developer/utilities/render/bloom.qml b/scripts/developer/utilities/render/bloom.qml index 52090348d9..705a9826d6 100644 --- a/scripts/developer/utilities/render/bloom.qml +++ b/scripts/developer/utilities/render/bloom.qml @@ -14,20 +14,11 @@ import "configSlider" Item { id: root - property var config: Render.getConfig("RenderMainView.Bloom") - property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold") property var configDebug: Render.getConfig("RenderMainView.DebugBloom") Column { spacing: 8 - CheckBox { - text: "Enable" - checked: root.config["enabled"] - onCheckedChanged: { - root.config["enabled"] = checked; - } - } GroupBox { title: "Debug" Row { @@ -88,35 +79,5 @@ Item { } } } - ConfigSlider { - label: "Intensity" - integral: false - config: root.config - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - height:38 - } - ConfigSlider { - label: "Size" - integral: false - config: root.config - property: "size" - max: 1.0 - min: 0.0 - width: 280 - height:38 - } - ConfigSlider { - label: "Threshold" - integral: false - config: root.configThreshold - property: "threshold" - max: 2.0 - min: 0.0 - width: 280 - height:38 - } } } diff --git a/scripts/developer/utilities/render/debugBloom.js b/scripts/developer/utilities/render/debugBloom.js index 3a508d351c..39629ab0ce 100644 --- a/scripts/developer/utilities/render/debugBloom.js +++ b/scripts/developer/utilities/render/debugBloom.js @@ -15,6 +15,6 @@ var window = new OverlayWindow({ title: 'Bloom', source: qml, width: 285, - height: 210, + height: 40, }); window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 9614f8b8fe..744150253d 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -678,6 +678,52 @@ +
+ + Bloom + +
+ Inherit + Off + On +
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 31984d2d01..de9027586e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -841,7 +841,7 @@ function loaded() { var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit"); var elZoneHazeModeDisabled = document.getElementById("property-zone-haze-mode-disabled"); var elZoneHazeModeEnabled = document.getElementById("property-zone-haze-mode-enabled"); - + var elZoneHazeRange = document.getElementById("property-zone-haze-range"); var elZoneHazeColor = document.getElementById("property-zone-haze-color"); var elZoneHazeColorRed = document.getElementById("property-zone-haze-color-red"); @@ -860,6 +860,15 @@ function loaded() { var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend"); + // Bloom + var elZoneBloomModeInherit = document.getElementById("property-zone-bloom-mode-inherit"); + var elZoneBloomModeDisabled = document.getElementById("property-zone-bloom-mode-disabled"); + var elZoneBloomModeEnabled = document.getElementById("property-zone-bloom-mode-enabled"); + + var elZoneBloomIntensity = document.getElementById("property-zone-bloom-intensity"); + var elZoneBloomThreshold = document.getElementById("property-zone-bloom-threshold"); + var elZoneBloomSize = document.getElementById("property-zone-bloom-size"); + var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); @@ -916,19 +925,19 @@ function loaded() { deleteJSONMaterialEditor(); } } - + elTypeIcon.style.display = "none"; elType.innerHTML = "No selection"; elPropertiesList.className = ''; - + elID.value = ""; elName.value = ""; elLocked.checked = false; elVisible.checked = false; - + elParentID.value = ""; elParentJointIndex.value = ""; - + elColorRed.value = ""; elColorGreen.value = ""; elColorBlue.value = ""; @@ -937,15 +946,15 @@ function loaded() { elPositionX.value = ""; elPositionY.value = ""; elPositionZ.value = ""; - + elRotationX.value = ""; elRotationY.value = ""; elRotationZ.value = ""; - + elDimensionsX.value = ""; elDimensionsY.value = ""; elDimensionsZ.value = ""; - + elRegistrationX.value = ""; elRegistrationY.value = ""; elRegistrationZ.value = ""; @@ -967,14 +976,14 @@ function loaded() { elAccelerationX.value = ""; elAccelerationY.value = ""; elAccelerationZ.value = ""; - + elRestitution.value = ""; elFriction.value = ""; elDensity.value = ""; - + elCollisionless.checked = false; elDynamic.checked = false; - + elCollideStatic.checked = false; elCollideKinematic.checked = false; elCollideDynamic.checked = false; @@ -991,27 +1000,27 @@ function loaded() { elCloneableGroup.style.display = "none"; elCloneableLimit.value = ""; elCloneableLifetime.value = ""; - - showElements(document.getElementsByClassName('can-cast-shadow-section'), true); + + showElements(document.getElementsByClassName('can-cast-shadow-section'), true); elCanCastShadow.checked = false; - + elCollisionSoundURL.value = ""; elLifetime.value = ""; elScriptURL.value = ""; elServerScripts.value = ""; elHyperlinkHref.value = ""; elDescription.value = ""; - + deleteJSONEditor(); elUserData.value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - + // Shape Properties elShape.value = "Cube"; setDropdownText(elShape); - + // Light Properties elLightSpotLight.checked = false; elLightColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; @@ -1022,7 +1031,7 @@ function loaded() { elLightFalloffRadius.value = ""; elLightExponent.value = ""; elLightCutoff.value = ""; - + // Model Properties elModelURL.value = ""; elCompoundShapeURL.value = ""; @@ -1039,7 +1048,7 @@ function loaded() { elModelAnimationAllowTranslation.checked = false; elModelTextures.value = ""; elModelOriginalTextures.value = ""; - + // Zone Properties elZoneFlyingAllowed.checked = false; elZoneGhostingAllowed.checked = false; @@ -1069,6 +1078,9 @@ function loaded() { elZoneHazeAltitudeEffect.checked = false; elZoneHazeBaseRef.value = ""; elZoneHazeCeiling.value = ""; + elZoneBloomIntensity.value = ""; + elZoneBloomThreshold.value = ""; + elZoneBloomSize.value = ""; elZoneSkyboxColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; elZoneSkyboxColorRed.value = ""; elZoneSkyboxColorGreen.value = ""; @@ -1078,7 +1090,8 @@ function loaded() { showElements(document.getElementsByClassName('skybox-section'), true); showElements(document.getElementsByClassName('ambient-section'), true); showElements(document.getElementsByClassName('haze-section'), true); - + showElements(document.getElementsByClassName('bloom-section'), true); + // Text Properties elTextText.value = ""; elTextLineHeight.value = ""; @@ -1091,14 +1104,14 @@ function loaded() { elTextBackgroundColorRed.value = ""; elTextBackgroundColorGreen.value = ""; elTextBackgroundColorBlue.value = ""; - + // Image Properties elImageURL.value = ""; - + // Web Properties elWebSourceURL.value = ""; elWebDPI.value = ""; - + // Material Properties elMaterialURL.value = ""; elParentMaterialNameNumber.value = ""; @@ -1109,13 +1122,13 @@ function loaded() { elMaterialMappingScaleX.value = ""; elMaterialMappingScaleY.value = ""; elMaterialMappingRot.value = ""; - + deleteJSONMaterialEditor(); elMaterialData.value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); - + disableProperties(); } else if (data.selections.length > 1) { deleteJSONEditor(); @@ -1252,7 +1265,7 @@ function loaded() { elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableLimit.value = properties.cloneLimit; elCloneableLifetime.value = properties.cloneLifetime; - + var grabbablesSet = false; var parsedUserData = {}; try { @@ -1478,6 +1491,14 @@ function loaded() { elZoneHazeBaseRef.value = properties.haze.hazeBaseRef.toFixed(0); elZoneHazeCeiling.value = properties.haze.hazeCeiling.toFixed(0); + elZoneBloomModeInherit.checked = (properties.bloomMode === 'inherit'); + elZoneBloomModeDisabled.checked = (properties.bloomMode === 'disabled'); + elZoneBloomModeEnabled.checked = (properties.bloomMode === 'enabled'); + + elZoneBloomIntensity.value = properties.bloom.bloomIntensity.toFixed(2); + elZoneBloomThreshold.value = properties.bloom.bloomThreshold.toFixed(2); + elZoneBloomSize.value = properties.bloom.bloomSize.toFixed(2); + elShapeType.value = properties.shapeType; elCompoundShapeURL.value = properties.compoundShapeURL; @@ -1504,6 +1525,9 @@ function loaded() { showElements(document.getElementsByClassName('haze-section'), elZoneHazeModeEnabled.checked); + + showElements(document.getElementsByClassName('bloom-section'), + elZoneBloomModeEnabled.checked); } else if (properties.type === "PolyVox") { elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); @@ -2069,6 +2093,18 @@ function loaded() { elZoneHazeBackgroundBlend.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBackgroundBlend')); + // Bloom + var bloomModeChanged = createZoneComponentModeChangedFunction('bloomMode', + elZoneBloomModeInherit, elZoneBloomModeDisabled, elZoneBloomModeEnabled); + + elZoneBloomModeInherit.addEventListener('change', bloomModeChanged); + elZoneBloomModeDisabled.addEventListener('change', bloomModeChanged); + elZoneBloomModeEnabled.addEventListener('change', bloomModeChanged); + + elZoneBloomIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomIntensity')); + elZoneBloomThreshold.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomThreshold')); + elZoneBloomSize.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomSize')); + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox', 'color', elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction);