From 321447ca6a13292e42f228bf54131f4862f3a28c Mon Sep 17 00:00:00 2001 From: Studio Date: Sun, 12 Jul 2015 14:44:38 -0400 Subject: [PATCH 01/22] Change default value of output buffer size frames to 20 as a default and added functionality to increase to 40. --- libraries/audio-client/src/AudioClient.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index aeea7c07c1..3a85adbc97 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -55,9 +55,9 @@ static const int NUM_AUDIO_CHANNELS = 2; -static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3; +static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20; static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1; -static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20; +static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 40; #if defined(Q_OS_ANDROID) || defined(Q_OS_WIN) static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false; #else From b93ae7b5273dcf305fc9ebdc5e57b7b3261641e2 Mon Sep 17 00:00:00 2001 From: "Kevin M. Thomas" Date: Wed, 15 Jul 2015 19:25:18 -0400 Subject: [PATCH 02/22] Move reload content item to developer menu. --- interface/src/Menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8b14830c2d..6fe3a4dff9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -255,8 +255,6 @@ Menu::Menu() { avatar, SLOT(updateMotionBehavior())); MenuWrapper* viewMenu = addMenu("View"); - - addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, @@ -329,6 +327,8 @@ Menu::Menu() { MenuWrapper* developerMenu = addMenu("Developer"); + addActionToQMenuAndActionHash(developerMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); + MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, // QML Qt::SHIFT | Qt::Key_A, From 671263909a6db098f1f28c49d828682e7b9acbfb Mon Sep 17 00:00:00 2001 From: "Kevin M. Thomas" Date: Wed, 15 Jul 2015 19:47:37 -0400 Subject: [PATCH 03/22] Moved reload content item to nested Network menu item. --- interface/src/Menu.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6fe3a4dff9..7315c02825 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -327,8 +327,6 @@ Menu::Menu() { MenuWrapper* developerMenu = addMenu("Developer"); - addActionToQMenuAndActionHash(developerMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); - MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, // QML Qt::SHIFT | Qt::Key_A, @@ -491,6 +489,7 @@ Menu::Menu() { #endif MenuWrapper* networkMenu = developerMenu->addMenu("Network"); + addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, From 9ac4e2f6877f9bd56112b40d1fc39064427c43a8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Jul 2015 18:22:27 -0700 Subject: [PATCH 04/22] Render spheres over avatar eyes if they're looking at me --- interface/src/avatar/Avatar.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3a55e73fa6..f9a1185e24 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -448,36 +448,35 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo } } - // Stack indicator spheres - float indicatorOffset = 0.0f; - if (!_displayName.isEmpty() && _displayNameAlpha != 0.0f) { - const float DISPLAY_NAME_INDICATOR_OFFSET = 0.22f; - indicatorOffset = DISPLAY_NAME_INDICATOR_OFFSET; - } - const float INDICATOR_RADIUS = 0.03f; - const float INDICATOR_INDICATOR_OFFSET = 3.0f * INDICATOR_RADIUS; - // If this is the avatar being looked at, render a little ball above their head if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { + const float INDICATOR_OFFSET = 0.22f; + const float INDICATOR_RADIUS = 0.03f; const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; - glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z); + glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + INDICATOR_OFFSET, _position.z); Transform transform; transform.setTranslation(position); batch.setModelTransform(transform); DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); - indicatorOffset += INDICATOR_INDICATOR_OFFSET; } - // If the avatar is looking at me, render an indication that they area + // If the avatar is looking at me, indicate that they are if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { - const glm::vec4 LOOKING_AT_ME_COLOR = { 0.8f, 0.65f, 0.0f, 0.1f }; - glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z); + const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; + float alpha = 1.0f; + float radius = 0.035f; Transform transform; + glm::vec3 position = getHead()->getLeftEyePosition(); transform.setTranslation(position); batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS, - 15, 15, LOOKING_AT_ME_COLOR); + DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + position = getHead()->getRightEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); } // quick check before falling into the code below: From bed266dfe993469cb6252fccf90a6e48e00767a8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Jul 2015 18:23:50 -0700 Subject: [PATCH 05/22] Fade looking-at-me eye spheres over half a second --- interface/src/avatar/Avatar.cpp | 31 ++++++++++++++++++------------- interface/src/avatar/Head.cpp | 4 ++++ interface/src/avatar/Head.h | 2 ++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f9a1185e24..884540bf7c 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -464,19 +464,24 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo // If the avatar is looking at me, indicate that they are if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; - float alpha = 1.0f; - float radius = 0.035f; - Transform transform; - glm::vec3 position = getHead()->getLeftEyePosition(); - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); - position = getHead()->getRightEyePosition(); - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + const float LOOKING_AT_ME_DURATION = 0.5f; // seconds + quint64 now = usecTimestampNow(); + float alpha = 1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) + / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND); + if (alpha > 0.0f) { + float radius = 0.035f; + Transform transform; + glm::vec3 position = getHead()->getLeftEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + position = getHead()->getRightEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + } } // quick check before falling into the code below: diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 43e68557ce..94a163e508 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -55,6 +55,7 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), + _isLookingAtMeStarted(0), _faceModel(this), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) @@ -323,6 +324,9 @@ glm::vec3 Head::getCorrectedLookAtPosition() { } void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { + if (!_isLookingAtMe) { + _isLookingAtMeStarted = usecTimestampNow(); + } _isLookingAtMe = true; _correctedLookAtPosition = correctedLookAtPosition; } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index d7a8462693..58f6f14b0a 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -53,6 +53,7 @@ public: glm::vec3 getCorrectedLookAtPosition(); void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } bool getIsLookingAtMe() { return _isLookingAtMe; } + quint64 getIsLookingAtMeStarted() { return _isLookingAtMeStarted; } float getScale() const { return _scale; } glm::vec3 getPosition() const { return _position; } @@ -139,6 +140,7 @@ private: bool _isCameraMoving; bool _isLookingAtMe; + quint64 _isLookingAtMeStarted; FaceModel _faceModel; glm::vec3 _correctedLookAtPosition; From 55683e0cd589d353b7367a0aaaace23203d42229 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Jul 2015 18:24:47 -0700 Subject: [PATCH 06/22] Size looking-at-me eye spheres per avatar model dimensions --- interface/src/avatar/Avatar.cpp | 14 +++++++++----- libraries/fbx/src/FBXReader.cpp | 9 ++++++++- libraries/fbx/src/FBXReader.h | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 884540bf7c..ba0e0126ba 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -469,18 +469,22 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo float alpha = 1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND); if (alpha > 0.0f) { - float radius = 0.035f; + const float RADIUS_INCREMENT = 0.005f; Transform transform; + glm::vec3 position = getHead()->getLeftEyePosition(); transform.setTranslation(position); batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + DependencyManager::get()->renderSolidSphere(batch, + getHead()->getFaceModel().getGeometry()->getFBXGeometry().leftEyeSize * _scale / 2.0f + RADIUS_INCREMENT, + 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + position = getHead()->getRightEyePosition(); transform.setTranslation(position); batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, radius, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + DependencyManager::get()->renderSolidSphere(batch, + getHead()->getFaceModel().getGeometry()->getFBXGeometry().rightEyeSize * _scale / 2.0f + RADIUS_INCREMENT, + 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); } } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 4d7bff4df0..466b3de3ee 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2616,10 +2616,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, buildModelMesh(extracted); # endif + if (extracted.mesh.isEye) { + if (maxJointIndex == geometry.leftEyeJointIndex) { + geometry.leftEyeSize = extracted.mesh.meshExtents.largestDimension() * offsetScale; + } else { + geometry.rightEyeSize = extracted.mesh.meshExtents.largestDimension() * offsetScale; + } + } + geometry.meshes.append(extracted.mesh); int meshIndex = geometry.meshes.size() - 1; meshIDsToMeshIndices.insert(it.key(), meshIndex); - } // now that all joints have been scanned, compute a collision shape for each joint diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 200cd4a121..3b3d90eb05 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -232,7 +232,10 @@ public: int rightHandJointIndex = -1; int leftToeJointIndex = -1; int rightToeJointIndex = -1; - + + float leftEyeSize = 0.0f; // Maximum mesh extents dimension + float rightEyeSize = 0.0f; + QVector humanIKJointIndices; glm::vec3 palmDirection; From 87dbbdb2e8a4c740f9c37b76cdd6fcba2c4983c5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Jul 2015 18:25:35 -0700 Subject: [PATCH 07/22] Set an initial alpha in anticipation of sphere alpha working --- interface/src/avatar/Avatar.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ba0e0126ba..6aa70196bd 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -464,10 +464,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo // If the avatar is looking at me, indicate that they are if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; + const float LOOKING_AT_ME_ALPHA_START = 0.8f; const float LOOKING_AT_ME_DURATION = 0.5f; // seconds quint64 now = usecTimestampNow(); - float alpha = 1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) - / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND); + float alpha = LOOKING_AT_ME_ALPHA_START + * (1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) + / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { const float RADIUS_INCREMENT = 0.005f; Transform transform; From e44cba500dc90ec00af72269be3a6df90dc06502 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 22 Jul 2015 09:52:49 -0700 Subject: [PATCH 08/22] Guard against head model not being available yet --- interface/src/avatar/Avatar.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6aa70196bd..e30c2ac24d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -471,22 +471,26 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo * (1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { - const float RADIUS_INCREMENT = 0.005f; - Transform transform; + QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); + if (geometry) { + const float RADIUS_INCREMENT = 0.005f; + Transform transform; - glm::vec3 position = getHead()->getLeftEyePosition(); - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, - getHead()->getFaceModel().getGeometry()->getFBXGeometry().leftEyeSize * _scale / 2.0f + RADIUS_INCREMENT, - 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + glm::vec3 position = getHead()->getLeftEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, + geometry->getFBXGeometry().leftEyeSize * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); - position = getHead()->getRightEyePosition(); - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, - getHead()->getFaceModel().getGeometry()->getFBXGeometry().rightEyeSize * _scale / 2.0f + RADIUS_INCREMENT, - 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + position = getHead()->getRightEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + DependencyManager::get()->renderSolidSphere(batch, + geometry->getFBXGeometry().rightEyeSize * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, + glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + + } } } From 9d33cfa9650eabbe07e54bcf027a8145ac083f7b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 22 Jul 2015 09:53:37 -0700 Subject: [PATCH 09/22] Allow for look-at positions being not quite up to date as avatars move So that looking-at-me indicators don't flicker on when they shouldn't. --- interface/src/avatar/Avatar.cpp | 4 ++-- interface/src/avatar/Head.cpp | 17 +++++++++++++---- interface/src/avatar/Head.h | 9 +++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e30c2ac24d..1fee720ad4 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -462,13 +462,13 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo } // If the avatar is looking at me, indicate that they are - if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { + if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; const float LOOKING_AT_ME_ALPHA_START = 0.8f; const float LOOKING_AT_ME_DURATION = 0.5f; // seconds quint64 now = usecTimestampNow(); float alpha = LOOKING_AT_ME_ALPHA_START - * (1.0f - ((float)(usecTimestampNow() - getHead()->getIsLookingAtMeStarted())) + * (1.0f - ((float)(usecTimestampNow() - getHead()->getLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 94a163e508..d4ef793ab3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -55,7 +55,8 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), - _isLookingAtMeStarted(0), + _lookingAtMeStarted(0), + _wasLastLookingAtMe(0), _faceModel(this), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) @@ -316,7 +317,7 @@ glm::quat Head::getFinalOrientationInLocalFrame() const { } glm::vec3 Head::getCorrectedLookAtPosition() { - if (_isLookingAtMe) { + if (isLookingAtMe()) { return _correctedLookAtPosition; } else { return getLookAtPosition(); @@ -324,13 +325,21 @@ glm::vec3 Head::getCorrectedLookAtPosition() { } void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { - if (!_isLookingAtMe) { - _isLookingAtMeStarted = usecTimestampNow(); + if (!isLookingAtMe()) { + _lookingAtMeStarted = usecTimestampNow(); } _isLookingAtMe = true; + _wasLastLookingAtMe = usecTimestampNow(); _correctedLookAtPosition = correctedLookAtPosition; } +bool Head::isLookingAtMe() { + // Allow for outages such as may be encountered during avatar movement + quint64 now = usecTimestampNow(); + const quint64 LOOKING_AT_ME_GAP_ALLOWED = 1000000; // microseconds + return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; +} + glm::quat Head::getCameraOrientation() const { // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so // you may wonder why this code is here. This method will be called while in Oculus mode to determine how diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 58f6f14b0a..a8161d1c7b 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -52,9 +52,9 @@ public: void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); glm::vec3 getCorrectedLookAtPosition(); void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } - bool getIsLookingAtMe() { return _isLookingAtMe; } - quint64 getIsLookingAtMeStarted() { return _isLookingAtMeStarted; } - + bool isLookingAtMe(); + quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; } + float getScale() const { return _scale; } glm::vec3 getPosition() const { return _position; } const glm::vec3& getEyePosition() const { return _eyePosition; } @@ -140,7 +140,8 @@ private: bool _isCameraMoving; bool _isLookingAtMe; - quint64 _isLookingAtMeStarted; + quint64 _lookingAtMeStarted; + quint64 _wasLastLookingAtMe; FaceModel _faceModel; glm::vec3 _correctedLookAtPosition; From 7c4f6b665bcd614cc6739fd1e28531621385cec4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 23 Jul 2015 11:04:58 -0700 Subject: [PATCH 10/22] Use a default eye diameter for models without eyes, e.g., the masks --- interface/src/avatar/Avatar.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bf41449f13..9f457e558d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -468,22 +468,29 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (alpha > 0.0f) { QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); if (geometry) { + const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye const float RADIUS_INCREMENT = 0.005f; Transform transform; glm::vec3 position = getHead()->getLeftEyePosition(); transform.setTranslation(position); batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, - geometry->getFBXGeometry().leftEyeSize * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + float eyeDiameter = geometry->getFBXGeometry().leftEyeSize; + if (eyeDiameter == 0.0f) { + eyeDiameter = DEFAULT_EYE_DIAMETER; + } + DependencyManager::get()->renderSolidSphere(batch, + eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); position = getHead()->getRightEyePosition(); transform.setTranslation(position); batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, - geometry->getFBXGeometry().rightEyeSize * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, - glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + eyeDiameter = geometry->getFBXGeometry().rightEyeSize; + if (eyeDiameter == 0.0f) { + eyeDiameter = DEFAULT_EYE_DIAMETER; + } + DependencyManager::get()->renderSolidSphere(batch, + eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); } } From 28e6a4ac6331f9810c300eb032b0ae44c4eb6d89 Mon Sep 17 00:00:00 2001 From: "Kevin M. Thomas" Date: Fri, 24 Jul 2015 09:36:35 -0400 Subject: [PATCH 11/22] Updating master as old job related to AudioClient.h was incorretly included with current job. --- libraries/audio-client/src/AudioClient.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 3a85adbc97..aeea7c07c1 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -55,9 +55,9 @@ static const int NUM_AUDIO_CHANNELS = 2; -static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20; +static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3; static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1; -static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 40; +static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20; #if defined(Q_OS_ANDROID) || defined(Q_OS_WIN) static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false; #else From 3893add1487143b6d79f1012f612a250242f29a1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 24 Jul 2015 10:43:24 -0700 Subject: [PATCH 12/22] Fix setting WebWindow's width and height Width and height parameters now set the WebWindow's width and height instead of its minimum size, when not part of a tool window. --- interface/src/scripting/WebWindowClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 3bd7e390ec..f187de95d2 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -57,7 +57,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid } else { auto dialogWidget = new QDialog(Application::getInstance()->getWindow(), Qt::Window); dialogWidget->setWindowTitle(title); - dialogWidget->setMinimumSize(width, height); + dialogWidget->resize(width, height); connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed); auto layout = new QVBoxLayout(dialogWidget); From 67c9a33cc0a0b57d79d957adb176c56976dbe68e Mon Sep 17 00:00:00 2001 From: "Kevin M. Thomas" Date: Fri, 24 Jul 2015 15:16:02 -0400 Subject: [PATCH 13/22] Update Menu.cpp --- interface/src/Menu.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d5f4d78f5b..91ae6a4d02 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -487,14 +487,10 @@ Menu::Menu() { #endif MenuWrapper* networkMenu = developerMenu->addMenu("Network"); -<<<<<<< HEAD addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); - addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); -======= addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false, qApp->getEntityEditPacketSender(), SLOT(toggleNackPackets())); ->>>>>>> f3dc159e336b7b580bbd39b367802b0099e66ccb addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, From 7be33dcb5845908254950206ce6cff794ff1edde Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Sat, 25 Jul 2015 11:40:58 -0400 Subject: [PATCH 14/22] Limit the amount of time consumed by rendering QML --- .../render-utils/src/OffscreenQmlSurface.cpp | 21 +++++++++++++------ .../render-utils/src/OffscreenQmlSurface.h | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 3ebc7704a8..056f9dbc6d 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -19,6 +19,7 @@ #include "FboCache.h" #include +#include class QMyQuickRenderControl : public QQuickRenderControl { protected: @@ -44,7 +45,10 @@ Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. -static const int SMALL_INTERVAL = 5; +static const int MAX_QML_FRAMERATE = 10; +static const int MIN_RENDER_INTERVAL_US = USECS_PER_SECOND / MAX_QML_FRAMERATE; +static const int MIN_TIMER_MS = 5; + OffscreenQmlSurface::OffscreenQmlSurface() : _renderControl(new QMyQuickRenderControl), _fboCache(new FboCache) { @@ -90,7 +94,6 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. _updateTimer.setSingleShot(true); - _updateTimer.setInterval(SMALL_INTERVAL); connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); // Now hook up the signals. For simplicy we don't differentiate between @@ -170,13 +173,18 @@ QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function MIN_RENDER_INTERVAL_US) { + _updateTimer.setInterval(MIN_TIMER_MS); + } else { + _updateTimer.setInterval((MIN_RENDER_INTERVAL_US - lastInterval) / USECS_PER_MSEC); + } _updateTimer.start(); } } @@ -243,6 +251,7 @@ void OffscreenQmlSurface::updateQuick() { if (_paused) { return; } + if (!makeCurrent()) { return; } @@ -270,11 +279,11 @@ void OffscreenQmlSurface::updateQuick() { // Need a debug context with sync logging to figure out why. // for now just clear the errors glGetError(); -// Q_ASSERT(!glGetError()); _quickWindow->resetOpenGLState(); QOpenGLFramebufferObject::bindDefault(); + _lastRenderTime = usecTimestampNow(); // Force completion of all the operations before we emit the texture as being ready for use glFinish(); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h index b892806c44..1fbf69ef4d 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.h +++ b/libraries/render-utils/src/OffscreenQmlSurface.h @@ -86,6 +86,7 @@ private: QQuickItem* _rootItem{ nullptr }; QTimer _updateTimer; FboCache* _fboCache; + quint64 _lastRenderTime{ 0 }; bool _polish{ true }; bool _paused{ true }; MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } }; From 24fff719c57d2a6daf565b9a5410b29fb8c431f1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 25 Jul 2015 21:11:23 -0700 Subject: [PATCH 15/22] quiet compiler --- interface/src/Application.cpp | 4 ---- interface/src/avatar/Avatar.cpp | 4 ++-- interface/src/avatar/MyAvatar.cpp | 1 - interface/src/devices/DdeFaceTracker.h | 5 ++--- interface/src/ui/ApplicationCompositor.cpp | 2 +- interface/src/ui/AudioStatsDialog.cpp | 6 ++++-- interface/src/ui/overlays/Cube3DOverlay.cpp | 1 - 7 files changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c2be31cfb3..fa2a82ecb4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3389,14 +3389,10 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi // set the bounds of rear mirror view gpu::Vec4i viewport; if (billboard) { - QSize size = DependencyManager::get()->getFrameBufferSize(); viewport = gpu::Vec4i(0, 0, region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device - QSize size = DependencyManager::get()->getFrameBufferSize(); float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - int x = region.x() * ratio; - int y = region.y() * ratio; int width = region.width() * ratio; int height = region.height() * ratio; viewport = gpu::Vec4i(0, 0, width, height); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9f457e558d..095a225952 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -462,8 +462,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float LOOKING_AT_ME_ALPHA_START = 0.8f; const float LOOKING_AT_ME_DURATION = 0.5f; // seconds quint64 now = usecTimestampNow(); - float alpha = LOOKING_AT_ME_ALPHA_START - * (1.0f - ((float)(usecTimestampNow() - getHead()->getLookingAtMeStarted())) + float alpha = LOOKING_AT_ME_ALPHA_START + * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c6c6919325..f332173568 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1290,7 +1290,6 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float driveLeft = _driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]; float targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; if (targetSpeed != 0.0f) { const float ROTATION_RAMP_TIMESCALE = 0.1f; diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 5536fa14bd..9673f541d2 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -91,13 +91,12 @@ private: int _leftBlinkIndex; int _rightBlinkIndex; - int _leftEyeOpenIndex; - int _rightEyeOpenIndex; - int _leftEyeDownIndex; int _rightEyeDownIndex; int _leftEyeInIndex; int _rightEyeInIndex; + int _leftEyeOpenIndex; + int _rightEyeOpenIndex; int _browDownLeftIndex; int _browDownRightIndex; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 54fb4fbd1f..9bda88b3bf 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -495,7 +495,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); // Get the angles, scaled between (-0.5,0.5) - float xAngle = (atan2(direction.z, direction.x) + PI_OVER_TWO); + float xAngle = (atan2f(direction.z, direction.x) + PI_OVER_TWO); float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); // Get the pixel range over which the xAngle and yAngle are scaled diff --git a/interface/src/ui/AudioStatsDialog.cpp b/interface/src/ui/AudioStatsDialog.cpp index 116cc60b5e..e57182e251 100644 --- a/interface/src/ui/AudioStatsDialog.cpp +++ b/interface/src/ui/AudioStatsDialog.cpp @@ -125,8 +125,10 @@ void AudioStatsDialog::renderStats() { audioInputBufferLatency = (double)_stats->getAudioInputMsecsReadStats().getWindowAverage(); inputRingBufferLatency = (double)_stats->getInputRungBufferMsecsAvailableStats().getWindowAverage(); networkRoundtripLatency = (double) audioMixerNodePointer->getPingMs(); - mixerRingBufferLatency = (double)_stats->getMixerAvatarStreamStats()._framesAvailableAverage * AudioConstants::NETWORK_FRAME_MSECS; - outputRingBufferLatency = (double)downstreamAudioStreamStats._framesAvailableAverage * AudioConstants::NETWORK_FRAME_MSECS; + mixerRingBufferLatency = (double)_stats->getMixerAvatarStreamStats()._framesAvailableAverage * + (double)AudioConstants::NETWORK_FRAME_MSECS; + outputRingBufferLatency = (double)downstreamAudioStreamStats._framesAvailableAverage * + (double)AudioConstants::NETWORK_FRAME_MSECS; audioOutputBufferLatency = (double)_stats->getAudioOutputMsecsUnplayedStats().getWindowAverage(); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 961d7f765b..200a1a328f 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -36,7 +36,6 @@ void Cube3DOverlay::render(RenderArgs* args) { // TODO: handle registration point?? glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); From 5cc0b45850fa489dc0439a2a1c40c4f5a3089bc8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 15 Jul 2015 18:27:39 -0700 Subject: [PATCH 16/22] Improved ParticleEffectEntityItem rendering and updates * Created custom pipelines and shaders for untextured and textured particle rendering. * Created custom render payload for particles * Moved all particle updates into simulation rather then render. * Uses pendingChanges.updateItem lambda to update the playload with new data for rendering. * ParticleEffectEntityItem now updates its dimensions properly, based on emitter properties. * Bug fix for dt not accumulating properly, during gaps between updates. now we just update all the time. (super cheap tho, if there are no particles animating) --- examples/particles.js | 7 +- libraries/entities-renderer/CMakeLists.txt | 2 + .../RenderableParticleEffectEntityItem.cpp | 327 ++++++++++++++---- .../src/RenderableParticleEffectEntityItem.h | 25 +- .../src/textured_particle.slf | 20 ++ .../src/textured_particle.slv | 28 ++ .../src/untextured_particle.slf | 16 + .../src/untextured_particle.slv | 24 ++ .../entities/src/ParticleEffectEntityItem.cpp | 80 ++++- .../entities/src/ParticleEffectEntityItem.h | 14 +- 10 files changed, 457 insertions(+), 86 deletions(-) create mode 100644 libraries/entities-renderer/src/textured_particle.slf create mode 100644 libraries/entities-renderer/src/textured_particle.slv create mode 100644 libraries/entities-renderer/src/untextured_particle.slf create mode 100644 libraries/entities-renderer/src/untextured_particle.slv diff --git a/examples/particles.js b/examples/particles.js index c26458a9af..deb6228fff 100644 --- a/examples/particles.js +++ b/examples/particles.js @@ -44,6 +44,7 @@ emitStrength: emitStrength, emitDirection: emitDirection, color: color, + lifespan: 1.0, visible: true, locked: false }); @@ -67,13 +68,13 @@ var objs = []; function Init() { objs.push(new TestBox()); - objs.push(new TestFx({ red: 255, blue: 0, green: 0 }, + objs.push(new TestFx({ red: 255, green: 0, blue: 0 }, { x: 0.5, y: 1.0, z: 0.0 }, 100, 3, 1)); - objs.push(new TestFx({ red: 0, blue: 255, green: 0 }, + objs.push(new TestFx({ red: 0, green: 255, blue: 0 }, { x: 0, y: 1, z: 0 }, 1000, 5, 0.5)); - objs.push(new TestFx({ red: 0, blue: 0, green: 255 }, + objs.push(new TestFx({ red: 0, green: 0, blue: 255 }, { x: -0.5, y: 1, z: 0 }, 100, 3, 1)); } diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 810d5f5843..e9adf2b750 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME entities-renderer) +AUTOSCRIBE_SHADER_LIB(gpu model render) + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Widgets OpenGL Network Script) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 48ac83dfc2..738a150dc5 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -14,22 +14,141 @@ #include #include #include +#include #include "EntitiesRendererLogging.h" #include "RenderableParticleEffectEntityItem.h" +#include "untextured_particle_vert.h" +#include "untextured_particle_frag.h" +#include "textured_particle_vert.h" +#include "textured_particle_frag.h" + +class ParticlePayload { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + typedef RenderableParticleEffectEntityItem::Vertex Vertex; + + ParticlePayload() : _vertexFormat(std::make_shared()), + _vertexBuffer(std::make_shared()), + _indexBuffer(std::make_shared()) { + _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0); + _vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv)); + _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba)); + } + + void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; } + const gpu::PipelinePointer& getPipeline() const { return _pipeline; } + + const Transform& getModelTransform() const { return _modelTransform; } + void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; } + + const AABox& getBound() const { return _bound; } + void setBound(AABox& bound) { _bound = bound; } + + gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; } + const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; } + + gpu::BufferPointer getIndexBuffer() { return _indexBuffer; } + const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; } + + void setTexture(gpu::TexturePointer texture) { _texture = texture; } + const gpu::TexturePointer& getTexture() const { return _texture; } + + bool getVisibleFlag() const { return _visibleFlag; } + void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; } + + void render(RenderArgs* args) const { + assert(_pipeline); + + gpu::Batch& batch = *args->_batch; + batch.setPipeline(_pipeline); + + if (_texture) { + batch.setResourceTexture(0, _texture); + } + + batch.setModelTransform(_modelTransform); + batch.setInputFormat(_vertexFormat); + batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex)); + batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0); + + auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t); + batch.drawIndexed(gpu::TRIANGLES, numIndices); + } + +protected: + Transform _modelTransform; + AABox _bound; + gpu::PipelinePointer _pipeline; + gpu::Stream::FormatPointer _vertexFormat; + gpu::BufferPointer _vertexBuffer; + gpu::BufferPointer _indexBuffer; + gpu::TexturePointer _texture; + bool _visibleFlag = true; +}; + +namespace render { + template <> + const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) { + if (payload->getVisibleFlag()) { + return ItemKey::Builder::transparentShape(); + } else { + return ItemKey::Builder().withInvisible().build(); + } + } + + template <> + const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) { + return payload->getBound(); + } + + template <> + void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) { + payload->render(args); + } +} + +gpu::PipelinePointer RenderableParticleEffectEntityItem::_texturedPipeline; +gpu::PipelinePointer RenderableParticleEffectEntityItem::_untexturedPipeline; + EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); } RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : ParticleEffectEntityItem(entityItemID, properties) { - _cacheID = DependencyManager::get()->allocateID(); + + // lazy creation of particle system pipeline + if (!_untexturedPipeline && !_texturedPipeline) { + createPipelines(); + } } -void RenderableParticleEffectEntityItem::render(RenderArgs* args) { - Q_ASSERT(getType() == EntityTypes::ParticleEffect); - PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render"); +bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, + render::ScenePointer scene, + render::PendingChanges& pendingChanges) { + + auto particlePayload = std::shared_ptr(new ParticlePayload()); + particlePayload->setPipeline(_untexturedPipeline); + _renderItemId = scene->allocateID(); + auto renderData = ParticlePayload::Pointer(particlePayload); + auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData)); + pendingChanges.resetItem(_renderItemId, renderPayload); + _scene = scene; + return true; +} + +void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self, + render::ScenePointer scene, + render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_renderItemId); + _scene = nullptr; +}; + +void RenderableParticleEffectEntityItem::update(const quint64& now) { + ParticleEffectEntityItem::update(now); if (_texturesChangedFlag) { if (_textures.isEmpty()) { @@ -42,71 +161,151 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { _texturesChangedFlag = false; } - bool textured = _texture && _texture->isLoaded(); - updateQuads(args, textured); - - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - if (textured) { - batch.setResourceTexture(0, _texture->getGPUTexture()); - } - batch.setModelTransform(getTransformToCenter()); - DependencyManager::get()->bindSimpleProgram(batch, textured); - DependencyManager::get()->renderVertices(batch, gpu::QUADS, _cacheID); -}; + updateRenderItem(); +} static glm::vec3 zSortAxis; static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) { return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis); } -void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool textured) { - float particleRadius = getParticleRadius(); - glm::vec4 particleColor(toGlm(getXColor()), getLocalRenderAlpha()); - - glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius; - glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius; - - QVector vertices; - QVector positions; - QVector textureCoords; - vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); - - if (textured) { - textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); - } - positions.reserve(getLivingParticleCount()); - - - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - positions.append(_particlePositions[i]); - if (textured) { - textureCoords.append(glm::vec2(0, 1)); - textureCoords.append(glm::vec2(1, 1)); - textureCoords.append(glm::vec2(1, 0)); - textureCoords.append(glm::vec2(0, 0)); - } - } - - // sort particles back to front - ::zSortAxis = args->_viewFrustum->getDirection(); - qSort(positions.begin(), positions.end(), zSort); - - for (int i = 0; i < positions.size(); i++) { - glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i]; - - // generate corners of quad aligned to face the camera. - vertices.append(pos + rightOffset + upOffset); - vertices.append(pos - rightOffset + upOffset); - vertices.append(pos - rightOffset - upOffset); - vertices.append(pos + rightOffset - upOffset); - - } - - if (textured) { - DependencyManager::get()->updateVertices(_cacheID, vertices, textureCoords, particleColor); - } else { - DependencyManager::get()->updateVertices(_cacheID, vertices, particleColor); - } +uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24); } +void RenderableParticleEffectEntityItem::updateRenderItem() { + + if (!_scene) + return; + + float particleRadius = getParticleRadius(); + auto xcolor = getXColor(); + auto alpha = (uint8_t)(glm::clamp(getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f); + auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha); + + // make a copy of each particle position + std::vector positions; + positions.reserve(getLivingParticleCount()); + for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { + positions.push_back(_particlePositions[i]); + } + + // sort particles back to front + // NOTE: this is view frustum might be one frame out of date. + auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); + ::zSortAxis = frustum->getDirection(); + qSort(positions.begin(), positions.end(), zSort); + + // allocate vertices + _vertices.clear(); + + // build vertices from particle positions + const glm::vec3 upOffset = frustum->getUp() * particleRadius; + const glm::vec3 rightOffset = frustum->getRight() * particleRadius; + for (auto&& pos : positions) { + // generate corners of quad aligned to face the camera. + _vertices.emplace_back(pos + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), rgba); + _vertices.emplace_back(pos - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), rgba); + _vertices.emplace_back(pos - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), rgba); + _vertices.emplace_back(pos + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), rgba); + } + + render::PendingChanges pendingChanges; + pendingChanges.updateItem(_renderItemId, [&](ParticlePayload& payload) { + + // update vertex buffer + auto vertexBuffer = payload.getVertexBuffer(); + size_t numBytes = sizeof(Vertex) * _vertices.size(); + vertexBuffer->resize(numBytes); + gpu::Byte* data = vertexBuffer->editData(); + memcpy(data, &(_vertices[0]), numBytes); + + // FIXME, don't update index buffer if num particles has not changed. + // update index buffer + auto indexBuffer = payload.getIndexBuffer(); + auto numQuads = (_vertices.size() / 4); + numBytes = sizeof(uint16_t) * numQuads * 6; + indexBuffer->resize(numBytes); + data = indexBuffer->editData(); + auto indexPtr = reinterpret_cast(data); + for (size_t i = 0; i < numQuads; ++i) { + indexPtr[i * 6 + 0] = i * 4 + 0; + indexPtr[i * 6 + 1] = i * 4 + 1; + indexPtr[i * 6 + 2] = i * 4 + 3; + indexPtr[i * 6 + 3] = i * 4 + 1; + indexPtr[i * 6 + 4] = i * 4 + 2; + indexPtr[i * 6 + 5] = i * 4 + 3; + } + + // update transform + glm::quat rot = _transform.getRotation(); + glm::vec3 pos = _transform.getTranslation(); + Transform t; + t.setRotation(rot); + t.setTranslation(pos); + payload.setModelTransform(t); + + // transform _particleMinBound and _particleMaxBound corners into world coords + glm::vec3 d = _particleMaxBound - _particleMinBound; + glm::vec3 corners[8] = { + pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)), + pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)), + pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)), + pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)), + pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)), + pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)), + pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)), + pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z)) + }; + glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX); + glm::vec3 max = -min; + for (int i = 0; i < 8; i++) { + min.x = std::min(min.x, corners[i].x); + min.y = std::min(min.y, corners[i].y); + min.z = std::min(min.z, corners[i].z); + max.x = std::max(max.x, corners[i].x); + max.y = std::max(max.y, corners[i].y); + max.z = std::max(max.z, corners[i].z); + } + AABox bound(min, max - min); + payload.setBound(bound); + + bool textured = _texture && _texture->isLoaded(); + if (textured) { + payload.setTexture(_texture->getGPUTexture()); + payload.setPipeline(_texturedPipeline); + } else { + payload.setTexture(nullptr); + payload.setPipeline(_untexturedPipeline); + } + }); + + _scene->enqueuePendingChanges(pendingChanges); +} + +void RenderableParticleEffectEntityItem::createPipelines() { + if (!_untexturedPipeline) { + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, + gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + gpu::State::BLEND_OP_ADD, gpu::State::ONE); + auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert))); + auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); + _untexturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + } + if (!_texturedPipeline) { + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, + gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + gpu::State::BLEND_OP_ADD, gpu::State::ONE); + auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert))); + auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); + _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + } +} diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 4ecea45ad0..9581c43ca5 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -16,20 +16,35 @@ #include "RenderableEntityItem.h" class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { +friend class ParticlePayload; public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - virtual void render(RenderArgs* args); - void updateQuads(RenderArgs* args, bool textured); + virtual void update(const quint64& now) override; - SIMPLE_RENDERABLE(); + void updateRenderItem(); + + virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges); protected: + render::ItemID _renderItemId; - int _cacheID; - const int VERTS_PER_PARTICLE = 4; + struct Vertex { + Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {} + glm::vec3 xyz; + glm::vec2 uv; + uint32_t rgba; + }; + static void createPipelines(); + + std::vector _vertices; + static gpu::PipelinePointer _untexturedPipeline; + static gpu::PipelinePointer _texturedPipeline; + + render::ScenePointer _scene; NetworkTexturePointer _texture; }; diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf new file mode 100644 index 0000000000..543aa643aa --- /dev/null +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -0,0 +1,20 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// fragment shader +// +// Copyright 2015 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 +// + +uniform sampler2D colorMap; + +varying vec4 varColor; +varying vec2 varTexCoord; + +void main(void) { + vec4 color = texture2D(colorMap, varTexCoord); + gl_FragColor = color * varColor; +} diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv new file mode 100644 index 0000000000..7564feb1ce --- /dev/null +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -0,0 +1,28 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// particle vertex shader +// +// Copyright 2015 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec4 varColor; +varying vec2 varTexCoord; + +void main(void) { + // pass along the color & uvs to fragment shader + varColor = gl_Color; + varTexCoord = gl_MultiTexCoord0.xy; + + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> +} diff --git a/libraries/entities-renderer/src/untextured_particle.slf b/libraries/entities-renderer/src/untextured_particle.slf new file mode 100644 index 0000000000..bb3ed77e3f --- /dev/null +++ b/libraries/entities-renderer/src/untextured_particle.slf @@ -0,0 +1,16 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// fragment shader +// +// Copyright 2015 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 +// + +varying vec4 varColor; + +void main(void) { + gl_FragColor = varColor; +} diff --git a/libraries/entities-renderer/src/untextured_particle.slv b/libraries/entities-renderer/src/untextured_particle.slv new file mode 100644 index 0000000000..2975dab046 --- /dev/null +++ b/libraries/entities-renderer/src/untextured_particle.slv @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// particle vertex shader +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec4 varColor; + +void main(void) { + // pass along the diffuse color + varColor = gl_Color; + + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> +} \ No newline at end of file diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 95effa2980..a687e2be7a 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -39,6 +39,7 @@ #include "EntityTree.h" #include "EntityTreeElement.h" #include "EntitiesLogging.h" +#include "EntityScriptingInterface.h" #include "ParticleEffectEntityItem.h" const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 }; @@ -92,6 +93,74 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte ParticleEffectEntityItem::~ParticleEffectEntityItem() { } +void ParticleEffectEntityItem::setDimensions(const glm::vec3& value) { + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::setLifespan(float lifespan) { + _lifespan = lifespan; + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::setEmitDirection(glm::vec3 emitDirection) { + _emitDirection = glm::normalize(emitDirection); + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::setEmitStrength(float emitStrength) { + _emitStrength = emitStrength; + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::setLocalGravity(float localGravity) { + _localGravity = localGravity; + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { + _particleRadius = particleRadius; + computeAndUpdateDimensions(); +} + +void ParticleEffectEntityItem::computeAndUpdateDimensions() { + + const float t = _lifespan * 1.1f; // add 10% extra time, to account for incremental timer accumulation error. + const float maxOffset = (0.5f * 0.25f * _emitStrength) + _particleRadius; + + // bounds for x and z is easy to compute because there is no at^2 term. + float xMax = (_emitDirection.x * _emitStrength + maxOffset) * t; + float xMin = (_emitDirection.x * _emitStrength - maxOffset) * t; + + float zMax = (_emitDirection.z * _emitStrength + maxOffset) * t; + float zMin = (_emitDirection.z * _emitStrength - maxOffset) * t; + + // yEnd is where the particle will end. + float a = _localGravity; + float atSquared = a * t * t; + float v = _emitDirection.y * _emitStrength + maxOffset; + float vt = v * t; + float yEnd = 0.5f * atSquared + vt; + + // yApex is where the particle is at it's apex. + float yApexT = (-v / a); + float yApex = 0.0f; + + // only set apex if it's within the lifespan of the particle. + if (yApexT >= 0.0f && yApexT <= t) { + yApex = -(v * v) / (2.0f * a); + } + + float yMax = std::max(yApex, yEnd); + float yMin = std::min(yApex, yEnd); + + // times 2 because dimensions are diameters not radii. + glm::vec3 dims(2.0f * std::max(fabs(xMin), fabs(xMax)), + 2.0f * std::max(fabs(yMin), fabs(yMax)), + 2.0f * std::max(fabs(zMin), fabs(zMax))); + + EntityItem::setDimensions(dims); +} + EntityItemProperties ParticleEffectEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class @@ -245,7 +314,7 @@ bool ParticleEffectEntityItem::isAnimatingSomething() const { } bool ParticleEffectEntityItem::needsToCallUpdate() const { - return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate(); + return true; } void ParticleEffectEntityItem::update(const quint64& now) { @@ -260,13 +329,6 @@ void ParticleEffectEntityItem::update(const quint64& now) { if (isAnimatingSomething()) { stepSimulation(deltaTime); - - // update the dimensions - glm::vec3 dims; - dims.x = glm::max(glm::abs(_particleMinBound.x), glm::abs(_particleMaxBound.x)) * 2.0f; - dims.y = glm::max(glm::abs(_particleMinBound.y), glm::abs(_particleMaxBound.y)) * 2.0f; - dims.z = glm::max(glm::abs(_particleMinBound.z), glm::abs(_particleMaxBound.z)) * 2.0f; - setDimensions(dims); } EntityItem::update(now); // let our base class handle it's updates... @@ -319,7 +381,7 @@ void ParticleEffectEntityItem::setAnimationSettings(const QString& value) { qCDebug(entities) << "ParticleEffectEntityItem::setAnimationSettings() calling setAnimationFrameIndex()..."; qCDebug(entities) << " settings:" << value; qCDebug(entities) << " settingsMap[frameIndex]:" << settingsMap["frameIndex"]; - qCDebug(entities" frameIndex: %20.5f", frameIndex); + qCDebug(entities, " frameIndex: %20.5f", frameIndex); } #endif diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 3136ab6c7c..994c609f0f 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -86,12 +86,14 @@ public: void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); } float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } + virtual void setDimensions(const glm::vec3& value) override; + static const quint32 DEFAULT_MAX_PARTICLES; void setMaxParticles(quint32 maxParticles); quint32 getMaxParticles() const { return _maxParticles; } static const float DEFAULT_LIFESPAN; - void setLifespan(float lifespan) { _lifespan = lifespan; } + void setLifespan(float lifespan); float getLifespan() const { return _lifespan; } static const float DEFAULT_EMIT_RATE; @@ -99,21 +101,23 @@ public: float getEmitRate() const { return _emitRate; } static const glm::vec3 DEFAULT_EMIT_DIRECTION; - void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = glm::normalize(emitDirection); } + void setEmitDirection(glm::vec3 emitDirection); const glm::vec3& getEmitDirection() const { return _emitDirection; } static const float DEFAULT_EMIT_STRENGTH; - void setEmitStrength(float emitStrength) { _emitStrength = emitStrength; } + void setEmitStrength(float emitStrength); float getEmitStrength() const { return _emitStrength; } static const float DEFAULT_LOCAL_GRAVITY; - void setLocalGravity(float localGravity) { _localGravity = localGravity; } + void setLocalGravity(float localGravity); float getLocalGravity() const { return _localGravity; } static const float DEFAULT_PARTICLE_RADIUS; - void setParticleRadius(float particleRadius) { _particleRadius = particleRadius; } + void setParticleRadius(float particleRadius); float getParticleRadius() const { return _particleRadius; } + void computeAndUpdateDimensions(); + bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); } float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); } float getAnimationFPS() const { return _animationLoop.getFPS(); } From 5844b594dcfcdee259c9c67d50994cda801cb663 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 27 Jul 2015 09:27:16 -0700 Subject: [PATCH 17/22] Converted magic numbers to constants. --- .../RenderableParticleEffectEntityItem.cpp | 26 +++++++++++-------- .../entities/src/ParticleEffectEntityItem.cpp | 3 ++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 738a150dc5..2b4626c2c3 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -175,8 +175,9 @@ uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { void RenderableParticleEffectEntityItem::updateRenderItem() { - if (!_scene) + if (!_scene) { return; + } float particleRadius = getParticleRadius(); auto xcolor = getXColor(); @@ -223,18 +224,20 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // FIXME, don't update index buffer if num particles has not changed. // update index buffer auto indexBuffer = payload.getIndexBuffer(); - auto numQuads = (_vertices.size() / 4); - numBytes = sizeof(uint16_t) * numQuads * 6; + const size_t NUM_VERTS_PER_PARTICLE = 4; + const size_t NUM_INDICES_PER_PARTICLE = 6; + auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE); + numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE; indexBuffer->resize(numBytes); data = indexBuffer->editData(); auto indexPtr = reinterpret_cast(data); for (size_t i = 0; i < numQuads; ++i) { - indexPtr[i * 6 + 0] = i * 4 + 0; - indexPtr[i * 6 + 1] = i * 4 + 1; - indexPtr[i * 6 + 2] = i * 4 + 3; - indexPtr[i * 6 + 3] = i * 4 + 1; - indexPtr[i * 6 + 4] = i * 4 + 2; - indexPtr[i * 6 + 5] = i * 4 + 3; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2; + indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3; } // update transform @@ -247,7 +250,8 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // transform _particleMinBound and _particleMaxBound corners into world coords glm::vec3 d = _particleMaxBound - _particleMinBound; - glm::vec3 corners[8] = { + const size_t NUM_BOX_CORNERS = 8; + glm::vec3 corners[NUM_BOX_CORNERS] = { pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)), pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)), pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)), @@ -259,7 +263,7 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { }; glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX); glm::vec3 max = -min; - for (int i = 0; i < 8; i++) { + for (size_t i = 0; i < NUM_BOX_CORNERS; i++) { min.x = std::min(min.x, corners[i].x); min.y = std::min(min.y, corners[i].y); min.z = std::min(min.z, corners[i].z); diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index a687e2be7a..71a5f87eb1 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -125,7 +125,8 @@ void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { void ParticleEffectEntityItem::computeAndUpdateDimensions() { const float t = _lifespan * 1.1f; // add 10% extra time, to account for incremental timer accumulation error. - const float maxOffset = (0.5f * 0.25f * _emitStrength) + _particleRadius; + const float MAX_RANDOM_FACTOR = (0.5f * 0.25); + const float maxOffset = (MAX_RANDOM_FACTOR * _emitStrength) + _particleRadius; // bounds for x and z is easy to compute because there is no at^2 term. float xMax = (_emitDirection.x * _emitStrength + maxOffset) * t; From 604ef5dc71311982e25a90bb80eba499b46524c9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 27 Jul 2015 09:28:56 -0700 Subject: [PATCH 18/22] Fixed float constant. --- libraries/entities/src/ParticleEffectEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 71a5f87eb1..4dfc9dd436 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -125,7 +125,7 @@ void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { void ParticleEffectEntityItem::computeAndUpdateDimensions() { const float t = _lifespan * 1.1f; // add 10% extra time, to account for incremental timer accumulation error. - const float MAX_RANDOM_FACTOR = (0.5f * 0.25); + const float MAX_RANDOM_FACTOR = (0.5f * 0.25f); const float maxOffset = (MAX_RANDOM_FACTOR * _emitStrength) + _particleRadius; // bounds for x and z is easy to compute because there is no at^2 term. From 9c57d1544fdc237fd4476972795e91050cdffa86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 27 Jul 2015 09:53:27 -0700 Subject: [PATCH 19/22] fix OctreeSceneStat unpacking in Application --- interface/src/Application.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fa2a82ecb4..c213d7629c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3596,19 +3596,13 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN int statsMessageLength = 0; const QUuid& nodeUUID = sendingNode->getUUID(); - OctreeSceneStats* octreeStats; - + // now that we know the node ID, let's add these stats to the stats for that node... _octreeSceneStatsLock.lockForWrite(); - auto it = _octreeServerSceneStats.find(nodeUUID); - if (it != _octreeServerSceneStats.end()) { - octreeStats = &it->second; - statsMessageLength = octreeStats->unpackFromPacket(packet); - } else { - OctreeSceneStats temp; - statsMessageLength = temp.unpackFromPacket(packet); - octreeStats = &temp; - } + + OctreeSceneStats* octreeStats = &_octreeServerSceneStats[nodeUUID]; + statsMessageLength = octreeStats->unpackFromPacket(packet); + _octreeSceneStatsLock.unlock(); VoxelPositionSize rootDetails; From 615218c77dc7c809a01da4265159805a81101da5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 27 Jul 2015 09:58:58 -0700 Subject: [PATCH 20/22] use a ref in stats unpacking --- interface/src/Application.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c213d7629c..69c10fc0ee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3600,13 +3600,13 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN // now that we know the node ID, let's add these stats to the stats for that node... _octreeSceneStatsLock.lockForWrite(); - OctreeSceneStats* octreeStats = &_octreeServerSceneStats[nodeUUID]; - statsMessageLength = octreeStats->unpackFromPacket(packet); + OctreeSceneStats& octreeStats = _octreeServerSceneStats[nodeUUID]; + statsMessageLength = octreeStats.unpackFromPacket(packet); _octreeSceneStatsLock.unlock(); VoxelPositionSize rootDetails; - voxelDetailsForCode(octreeStats->getJurisdictionRoot(), rootDetails); + voxelDetailsForCode(octreeStats.getJurisdictionRoot(), rootDetails); // see if this is the first we've heard of this node... NodeToJurisdictionMap* jurisdiction = NULL; @@ -3631,7 +3631,7 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN // but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the // details from the OctreeSceneStats to construct the JurisdictionMap JurisdictionMap jurisdictionMap; - jurisdictionMap.copyContents(octreeStats->getJurisdictionRoot(), octreeStats->getJurisdictionEndNodes()); + jurisdictionMap.copyContents(octreeStats.getJurisdictionRoot(), octreeStats.getJurisdictionEndNodes()); jurisdiction->lockForWrite(); (*jurisdiction)[nodeUUID] = jurisdictionMap; jurisdiction->unlock(); From d3c6d8b3ccd528297de8f7e517499be4171105ef Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 27 Jul 2015 10:06:34 -0700 Subject: [PATCH 21/22] move call to get VoxelPositionSize inside debug that uses it --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 69c10fc0ee..bb564824b0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3605,9 +3605,6 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN _octreeSceneStatsLock.unlock(); - VoxelPositionSize rootDetails; - voxelDetailsForCode(octreeStats.getJurisdictionRoot(), rootDetails); - // see if this is the first we've heard of this node... NodeToJurisdictionMap* jurisdiction = NULL; QString serverType; @@ -3619,6 +3616,9 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN jurisdiction->lockForRead(); if (jurisdiction->find(nodeUUID) == jurisdiction->end()) { jurisdiction->unlock(); + + VoxelPositionSize rootDetails; + voxelDetailsForCode(octreeStats.getJurisdictionRoot(), rootDetails); qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]", qPrintable(serverType), From ceffbb6383fae7df2d722a24c39622c87169ee23 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 27 Jul 2015 11:41:53 -0700 Subject: [PATCH 22/22] Update edit.js to show an alert window when new objects would be out of bounds --- examples/edit.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 1af016958f..ec3106e585 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -329,7 +329,7 @@ var toolBar = (function () { Script.setTimeout(resize, RESIZE_INTERVAL); } else { - print("Can't add model: Model would be out of bounds."); + Window.alert("Can't add model: Model would be out of bounds."); } } @@ -374,7 +374,7 @@ var toolBar = (function () { }); } else { - print("Can't create box: Box would be out of bounds."); + Window.alert("Can't create box: Box would be out of bounds."); } return true; } @@ -390,7 +390,7 @@ var toolBar = (function () { color: { red: 255, green: 0, blue: 0 } }); } else { - print("Can't create sphere: Sphere would be out of bounds."); + Window.alert("Can't create sphere: Sphere would be out of bounds."); } return true; } @@ -413,7 +413,7 @@ var toolBar = (function () { cutoff: 180, // in degrees }); } else { - print("Can't create Light: Light would be out of bounds."); + Window.alert("Can't create Light: Light would be out of bounds."); } return true; } @@ -433,7 +433,7 @@ var toolBar = (function () { lineHeight: 0.06 }); } else { - print("Can't create box: Text would be out of bounds."); + Window.alert("Can't create box: Text would be out of bounds."); } return true; } @@ -449,7 +449,7 @@ var toolBar = (function () { sourceUrl: "https://highfidelity.com/", }); } else { - print("Can't create Web Entity: would be out of bounds."); + Window.alert("Can't create Web Entity: would be out of bounds."); } return true; } @@ -464,7 +464,7 @@ var toolBar = (function () { dimensions: { x: 10, y: 10, z: 10 }, }); } else { - print("Can't create box: Text would be out of bounds."); + Window.alert("Can't create box: Text would be out of bounds."); } return true; } @@ -482,7 +482,7 @@ var toolBar = (function () { voxelSurfaceStyle: 1 }); } else { - print("Can't create PolyVox: would be out of bounds."); + Window.alert("Can't create PolyVox: would be out of bounds."); } return true; } @@ -1068,13 +1068,16 @@ function importSVO(importURL) { if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { position = getPositionToCreateEntity(); } - var pastedEntityIDs = Clipboard.pasteEntities(position); - - if (isActive) { - selectionManager.setSelections(pastedEntityIDs); - } + if (position.x > 0 && position.y > 0 && position.z > 0) { + var pastedEntityIDs = Clipboard.pasteEntities(position); + if (isActive) { + selectionManager.setSelections(pastedEntityIDs); + } Window.raiseMainWindow(); + } else { + Window.alert("Can't import objects: objects would be out of bounds."); + } } else { Window.alert("There was an error importing the entity file."); }