From bbb5f832a2abcd79e9989d3bb39e5fd3b0890c47 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 15 Jul 2015 15:11:16 -0700 Subject: [PATCH 001/109] Empty Rig. --- libraries/animation/src/Rig.cpp | 9 +++++++++ libraries/animation/src/Rig.h | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 libraries/animation/src/Rig.cpp create mode 100644 libraries/animation/src/Rig.h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp new file mode 100644 index 0000000000..b6cda25966 --- /dev/null +++ b/libraries/animation/src/Rig.cpp @@ -0,0 +1,9 @@ +// +// Rig.cpp +// hifi +// +// Created by Howard Stearns on 7/15/15. +// +// + +#include "Rig.h" diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h new file mode 100644 index 0000000000..3ccea315b8 --- /dev/null +++ b/libraries/animation/src/Rig.h @@ -0,0 +1,14 @@ +// +// Rig.h +// hifi +// +// Created by Howard Stearns on 7/15/15. +// +// + +#ifndef __hifi__Rig__ +#define __hifi__Rig__ + +#include + +#endif /* defined(__hifi__Rig__) */ From 5312993e56ea34ebfc555f8ebccd9f6d1537acf4 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 15 Jul 2015 15:30:34 -0700 Subject: [PATCH 002/109] Hmm, last was missing some emptiness. --- libraries/animation/src/Rig.cpp | 7 +++++-- libraries/animation/src/Rig.h | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b6cda25966..c8e86319cb 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1,9 +1,12 @@ // // Rig.cpp -// hifi +// libraries/script-engine/src/ // -// Created by Howard Stearns on 7/15/15. +// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Rig.h" diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 3ccea315b8..df4fe31d84 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -1,14 +1,23 @@ // // Rig.h -// hifi +// libraries/script-engine/src/ // -// Created by Howard Stearns on 7/15/15. +// Produces animation data and hip placement for the current timestamp. // +// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// 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__Rig__ #define __hifi__Rig__ -#include +#include + +class AnimationObject : public QObject { + +}; #endif /* defined(__hifi__Rig__) */ From 6ca458b62469038f2c1b8b769611e5de0876b395 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 15 Jul 2015 15:59:24 -0700 Subject: [PATCH 003/109] Instantiated in MyAvatar --- interface/src/avatar/MyAvatar.h | 2 ++ libraries/animation/src/Rig.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a2566118f2..0d94d327a9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -14,6 +14,7 @@ #include #include +#include #include "Avatar.h" @@ -285,6 +286,7 @@ private: QString _bodyModelName; QString _fullAvatarModelName; + Rig _rig; // used for rendering when in first person view or when in an HMD. SkeletonModel _firstPersonSkeletonModel; bool _prevShouldDrawHead; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index df4fe31d84..3e9307385b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -16,7 +16,7 @@ #include -class AnimationObject : public QObject { +class Rig : public QObject { }; From b87ed7dc189c3add5bb149f11164900ba44f871a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 15 Jul 2015 17:05:57 -0700 Subject: [PATCH 004/109] Remove dead code. --- interface/src/avatar/Hand.cpp | 53 ----------------------------------- interface/src/avatar/Hand.h | 7 ----- interface/src/avatar/Head.h | 1 - 3 files changed, 61 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 7b2968973c..aee032aa93 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -14,7 +14,6 @@ #include #include -#include #include "AvatarManager.h" #include "Hand.h" @@ -43,58 +42,6 @@ void Hand::simulate(float deltaTime, bool isMine) { } } -// We create a static CollisionList that is recycled for each collision test. -const float MAX_COLLISIONS_PER_AVATAR = 32; -static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR); - -void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { - if (!avatar || avatar == _owningAvatar) { - // don't collide hands against ourself (that is done elsewhere) - return; - } - - const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel(); - int jointIndices[2]; - jointIndices[0] = skeletonModel.getLeftHandJointIndex(); - jointIndices[1] = skeletonModel.getRightHandJointIndex(); - - for (size_t i = 0; i < 2; i++) { - int jointIndex = jointIndices[i]; - if (jointIndex < 0) { - continue; - } - - handCollisions.clear(); - QVector shapes; - skeletonModel.getHandShapes(jointIndex, shapes); - - if (avatar->findCollisions(shapes, handCollisions)) { - glm::vec3 totalPenetration(0.0f); - glm::vec3 averageContactPoint; - for (int j = 0; j < handCollisions.size(); ++j) { - CollisionInfo* collision = handCollisions.getCollision(j); - totalPenetration += collision->_penetration; - averageContactPoint += collision->_contactPoint; - } - if (isMyHand) { - // our hand against other avatar - // TODO: resolve this penetration when we don't think the other avatar will yield - //palm.addToPenetration(averagePenetration); - } else { - // someone else's hand against MyAvatar - // TODO: submit collision info to MyAvatar which should lean accordingly - averageContactPoint /= (float)handCollisions.size(); - avatar->applyCollision(averageContactPoint, totalPenetration); - - CollisionInfo collision; - collision._penetration = totalPenetration; - collision._contactPoint = averageContactPoint; - emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision); - } - } - } -} - void Hand::resolvePenetrations() { for (size_t i = 0; i < getNumPalms(); ++i) { PalmData& palm = getPalms()[i]; diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index f6991c5a55..5e070d53e0 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -30,11 +30,6 @@ class Avatar; -class ProgramObject; - -const float HAND_PADDLE_OFFSET = 0.1f; -const float HAND_PADDLE_THICKNESS = 0.01f; -const float HAND_PADDLE_RADIUS = 0.15f; class Hand : public HandData { public: @@ -43,8 +38,6 @@ public: void simulate(float deltaTime, bool isMine); void render(RenderArgs* renderArgs, bool isMine); - void collideAgainstAvatar(Avatar* avatar, bool isMyHand); - void resolvePenetrations(); private: diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 3f839d53bc..2baa16f90c 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -26,7 +26,6 @@ const float EYE_EAR_GAP = 0.08f; class Avatar; -class ProgramObject; class Head : public HeadData { public: From a529cce626540513090b59b2730cf7c1894050e5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 16 Jul 2015 14:36:28 -0700 Subject: [PATCH 005/109] Empty test suite and instructions. --- tests/rig/CMakeLists.txt | 9 ++++++ tests/rig/src/RigTests.cpp | 31 +++++++++++++++++++++ tests/rig/src/RigTests.h | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/rig/CMakeLists.txt create mode 100644 tests/rig/src/RigTests.cpp create mode 100644 tests/rig/src/RigTests.h diff --git a/tests/rig/CMakeLists.txt b/tests/rig/CMakeLists.txt new file mode 100644 index 0000000000..abf5da12c2 --- /dev/null +++ b/tests/rig/CMakeLists.txt @@ -0,0 +1,9 @@ +# Declare dependencies +macro (setup_testcase_dependencies) + # link in the shared libraries + link_hifi_libraries(shared animation) + + copy_dlls_beside_windows_executable() +endmacro () + +setup_hifi_testcase() diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp new file mode 100644 index 0000000000..c32e136fd1 --- /dev/null +++ b/tests/rig/src/RigTests.cpp @@ -0,0 +1,31 @@ +// +// RigTests.cpp +// tests/rig/src +// +// Created by Howard Stearns on 6/16/15 +// 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 +#include "RigTests.h" + +QTEST_MAIN(RigTests) + +void RigTests::initTestCase() { + _rig = new Rig(); +} + +void RigTests::dummyPassTest() { + bool x = true; + std::cout << "dummyPassTest x=" << x << std::endl; + QCOMPARE(x, true); +} + +void RigTests::dummyFailTest() { + bool x = false; + std::cout << "dummyFailTest x=" << x << std::endl; + QCOMPARE(x, true); +} diff --git a/tests/rig/src/RigTests.h b/tests/rig/src/RigTests.h new file mode 100644 index 0000000000..ddb8de9c2d --- /dev/null +++ b/tests/rig/src/RigTests.h @@ -0,0 +1,56 @@ +// +// RigTests.h +// tests/rig/src +// +// Created by Howard Stearns on 6/16/15 +// 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 +// + +#ifndef hifi_RigTests_h +#define hifi_RigTests_h + +#include +#include + +#include "../QTestExtensions.h" + + +// The QTest terminology is not consistent with itself or with industry: +// The whole directory, and the rig-tests target, doesn't seem to be a QTest concept, an corresponds roughly to a toplevel suite of suites. +// The directory can contain any number of classes like this one. (Don't forget to wipe your build dir, and rerun cmake when you add one.): +// QTest doc (http://doc.qt.io/qt-5/qtest-overview.html) calls this a "test case". +// The output of QTest's 'ctest' runner calls this a "test" when run in the whole directory (e.g., when reporting success/failure counts). +// The test case (like this class) can contain any number of test slots: +// QTest doc calls these "test functions" +// When you run a single test case executable (e.g., "rig-RigTests"), the (unlabeled) count includes these test functions and also the before method, which is auto generated as initTestCase. + +// To build and run via make: +// make help | grep tests # shows all test targets, including all-tests and rig-tests. +// make all-tests # will compile and then die as soon as any test case dies, even if its not in your directory +// make rig-tests # will compile and run `ctest .` in the tests/rig directory, running all the test cases found there. +// Alas, only summary output is shown on stdout. The real results, including any stdout that your code does, is in tests/rig/Testing/Temporary/LastTest.log, or... +// tests/rig/rig-RigTests (or the executable corresponding to any test case you define here) will run just that case and give output directly. +// +// To build and run via Xcode: +// On some machines, xcode can't find cmake on the path it uses. I did, effectively: sudo ln -s `which cmake` /usr/bin +// Note the above make instructions. +// all-tests, rig-tests, and rig-RigTests are all targets: +// The first two of these show no output at all, but if there's a failure you can see it by clicking on the red failure in the "issue navigator" (or by externally viewing the .log above). +// The last (or any other individual test case executable) does show output in the Xcode output display. + +class RigTests : public QObject { + Q_OBJECT + + private slots: + void initTestCase(); + void dummyPassTest(); + void dummyFailTest(); + + private: + Rig* _rig; +}; + +#endif // hifi_RigTests_h From bcd6b30ec3c3c7c53a7b3b8a0de0ca1be5130ad0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 17 Jul 2015 13:31:48 -0700 Subject: [PATCH 006/109] move AnimationHandle from render-utils to animation. give Rig some jointstates and animation lists --- interface/src/Camera.h | 2 +- interface/src/avatar/FaceModel.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 6 +- interface/src/ui/AnimationsDialog.cpp | 2 +- interface/src/ui/overlays/Cube3DOverlay.cpp | 1 - .../src/ui/overlays/LocalModelsOverlay.cpp | 2 +- .../src/AnimationHandle.cpp | 100 +++++++++--------- .../src/AnimationHandle.h | 75 ++++++++----- .../src/JointState.cpp | 0 .../src/JointState.h | 0 libraries/animation/src/Rig.cpp | 81 ++++++++++++++ libraries/animation/src/Rig.h | 33 +++++- libraries/render-utils/src/Model.cpp | 15 +-- libraries/render-utils/src/Model.h | 1 - 14 files changed, 220 insertions(+), 99 deletions(-) rename libraries/{render-utils => animation}/src/AnimationHandle.cpp (69%) rename libraries/{render-utils => animation}/src/AnimationHandle.h (69%) rename libraries/{render-utils => animation}/src/JointState.cpp (100%) rename libraries/{render-utils => animation}/src/JointState.h (100%) diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 6eed39cf16..bddb01ef21 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -28,7 +28,7 @@ enum CameraMode }; Q_DECLARE_METATYPE(CameraMode); -static int cameraModeId = qRegisterMetaType(); +// static int cameraModeId = qRegisterMetaType(); class Camera : public QObject { Q_OBJECT diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 170965bb4d..16b370d459 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -49,7 +49,6 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 053ce7b3cc..537b6f339a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -488,7 +488,7 @@ void MyAvatar::loadLastRecording() { } AnimationHandlePointer MyAvatar::addAnimationHandle() { - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); + AnimationHandlePointer handle = _rig.createAnimationHandle(); _animationHandles.append(handle); return handle; } @@ -506,7 +506,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); + AnimationHandlePointer handle = _rig.createAnimationHandle(); handle->setURL(url); handle->setFPS(fps); handle->setPriority(priority); @@ -534,7 +534,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo } } // no joy; use the parameters provided - AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); + AnimationHandlePointer handle = _rig.createAnimationHandle(); handle->setRole(role); handle->setURL(url); handle->setFPS(fps); diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index 0428e79e6f..5960ffc1fa 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -156,7 +156,7 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo buttons->addWidget(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); - _stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); + _stop->connect(_handle.get(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); _stop->setEnabled(_handle->isRunning()); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 7f6fd5f971..6c32281e75 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -35,7 +35,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(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 06b27f8f22..7f86e339e8 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -30,7 +30,7 @@ void LocalModelsOverlay::update(float deltatime) { void LocalModelsOverlay::render(RenderArgs* args) { if (_visible) { - float glowLevel = getGlowLevel(); // FIXME, glowing removed for now + // float glowLevel = getGlowLevel(); // FIXME, glowing removed for now auto batch = args ->_batch; Application* app = Application::getInstance(); diff --git a/libraries/render-utils/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp similarity index 69% rename from libraries/render-utils/src/AnimationHandle.cpp rename to libraries/animation/src/AnimationHandle.cpp index 6ad6730952..7f45fb600e 100644 --- a/libraries/render-utils/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -10,7 +10,7 @@ // #include "AnimationHandle.h" -#include "Model.h" + void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { @@ -20,28 +20,17 @@ void AnimationHandle::setURL(const QUrl& url) { } } -static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { - for (QList::iterator it = handles.begin(); it != handles.end(); it++) { - if (handle->getPriority() > (*it)->getPriority()) { - handles.insert(it, handle); - return; - } - } - handles.append(handle); -} - void AnimationHandle::setPriority(float priority) { if (_priority == priority) { return; } if (isRunning()) { - _model->_runningAnimations.removeOne(_self); + _rig->removeRunningAnimation(getAnimationHandlePointer()); if (priority < _priority) { replaceMatchingPriorities(priority); } _priority = priority; - insertSorted(_model->_runningAnimations, _self); - + _rig->addRunningAnimation(getAnimationHandlePointer()); } else { _priority = priority; } @@ -68,21 +57,21 @@ void AnimationHandle::setRunning(bool running) { } _animationLoop.setRunning(running); if (isRunning()) { - if (!_model->_runningAnimations.contains(_self)) { - insertSorted(_model->_runningAnimations, _self); + if (!_rig->isRunningAnimation(getAnimationHandlePointer())) { + _rig->addRunningAnimation(getAnimationHandlePointer()); } } else { - _model->_runningAnimations.removeOne(_self); + _rig->removeRunningAnimation(getAnimationHandlePointer()); restoreJoints(); replaceMatchingPriorities(0.0f); } emit runningChanged(isRunning()); } -AnimationHandle::AnimationHandle(Model* model) : - QObject(model), - _model(model), - _priority(1.0f) +AnimationHandle::AnimationHandle(RigPointer rig) : + QObject(rig.get()), + _rig(rig), + _priority(1.0f) { } @@ -110,42 +99,52 @@ void AnimationHandle::setAnimationDetails(const AnimationDetails& details) { } +void AnimationHandle::setJointMappings(QVector jointMappings) { + _jointMappings = jointMappings; +} + + void AnimationHandle::simulate(float deltaTime) { if (!_animation || !_animation->isLoaded()) { return; } - + _animationLoop.simulate(deltaTime); - - // update the joint mappings if necessary/possible + if (_jointMappings.isEmpty()) { - if (_model && _model->isActive()) { - _jointMappings = _model->getGeometry()->getJointMappings(_animation); - } - if (_jointMappings.isEmpty()) { - return; - } - if (!_maskedJoints.isEmpty()) { - const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); - for (int i = 0; i < _jointMappings.size(); i++) { - int& mapping = _jointMappings[i]; - if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { - mapping = -1; - } - } - } + qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()"; + return; } - + + // // update the joint mappings if necessary/possible + // if (_jointMappings.isEmpty()) { + // if (_model && _model->isActive()) { + // _jointMappings = _model->getGeometry()->getJointMappings(_animation); + // } + // if (_jointMappings.isEmpty()) { + // return; + // } + // if (!_maskedJoints.isEmpty()) { + // const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); + // for (int i = 0; i < _jointMappings.size(); i++) { + // int& mapping = _jointMappings[i]; + // if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { + // mapping = -1; + // } + // } + // } + // } + const FBXGeometry& animationGeometry = _animation->getGeometry(); if (animationGeometry.animationFrames.isEmpty()) { stop(); return; } - + if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) { _animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size()); } - + // blend between the closest two frames applyFrame(getFrameIndex()); } @@ -154,17 +153,21 @@ void AnimationHandle::applyFrame(float frameIndex) { if (!_animation || !_animation->isLoaded()) { return; } - + const FBXGeometry& animationGeometry = _animation->getGeometry(); int frameCount = animationGeometry.animationFrames.size(); const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); float frameFraction = glm::fract(frameIndex); + QVector jointStates = _rig->getJointStates(); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; - state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority); + JointState& state = jointStates[mapping]; + state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), + ceilFrame.rotations.at(i), + frameFraction), + _priority); } } } @@ -173,7 +176,8 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; + QVector jointStates = _rig->getJointStates(); + JointState& state = jointStates[mapping]; if (_priority == state._animationPriority) { state._animationPriority = newPriority; } @@ -185,9 +189,9 @@ void AnimationHandle::restoreJoints() { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = _model->_jointStates[mapping]; + QVector jointStates = _rig->getJointStates(); + JointState& state = jointStates[mapping]; state.restoreRotation(1.0f, state._animationPriority); } } } - diff --git a/libraries/render-utils/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h similarity index 69% rename from libraries/render-utils/src/AnimationHandle.h rename to libraries/animation/src/AnimationHandle.h index ca9c2eb6d0..a8c9d800a4 100644 --- a/libraries/render-utils/src/AnimationHandle.h +++ b/libraries/animation/src/AnimationHandle.h @@ -18,22 +18,43 @@ #include #include -#include -#include +#include "AnimationCache.h" +#include "AnimationLoop.h" +#include "Rig.h" class AnimationHandle; class Model; -typedef QSharedPointer AnimationHandlePointer; -typedef QWeakPointer WeakAnimationHandlePointer; +typedef std::shared_ptr AnimationHandlePointer; +typedef std::weak_ptr WeakAnimationHandlePointer; +inline uint qHash(const std::shared_ptr& a, uint seed) { + // return qHash(a.get(), seed); + AnimationHandle* strongRef = a ? a.get() : nullptr; + return qHash(strongRef, seed); +} +inline uint qHash(const std::weak_ptr& a, uint seed) { + AnimationHandlePointer strongPointer = a.lock(); + AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr; + return qHash(strongRef, seed); +} + + +// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) { +// return qHash(handle.data(), seed); +// } + /// Represents a handle to a model animation. -class AnimationHandle : public QObject { +class AnimationHandle : public QObject, public std::enable_shared_from_this { Q_OBJECT - + public: + AnimationHandle(RigPointer rig); + + AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); } + void setRole(const QString& role) { _role = role; } const QString& getRole() const { return _role; } @@ -45,26 +66,26 @@ public: void setMaskedJoints(const QStringList& maskedJoints); const QStringList& getMaskedJoints() const { return _maskedJoints; } - + void setFPS(float fps) { _animationLoop.setFPS(fps); } float getFPS() const { return _animationLoop.getFPS(); } void setLoop(bool loop) { _animationLoop.setLoop(loop); } bool getLoop() const { return _animationLoop.getLoop(); } - + void setHold(bool hold) { _animationLoop.setHold(hold); } bool getHold() const { return _animationLoop.getHold(); } - + void setStartAutomatically(bool startAutomatically); bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); } - + void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); } float getFirstFrame() const { return _animationLoop.getFirstFrame(); } - + void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); } float getLastFrame() const { return _animationLoop.getLastFrame(); } - + void setRunning(bool running); bool isRunning() const { return _animationLoop.isRunning(); } @@ -74,30 +95,25 @@ public: AnimationDetails getAnimationDetails() const; void setAnimationDetails(const AnimationDetails& details); + void setJointMappings(QVector jointMappings); + void simulate(float deltaTime); + void applyFrame(float frameIndex); + void replaceMatchingPriorities(float newPriority); + void restoreJoints(); + void clearJoints() { _jointMappings.clear(); } + signals: - + void runningChanged(bool running); public slots: void start() { setRunning(true); } void stop() { setRunning(false); } - + private: - friend class Model; - - AnimationHandle(Model* model); - - void simulate(float deltaTime); - void applyFrame(float frameIndex); - void replaceMatchingPriorities(float newPriority); - void restoreJoints(); - - void clearJoints() { _jointMappings.clear(); } - - Model* _model; - WeakAnimationHandlePointer _self; + RigPointer _rig; AnimationPointer _animation; QString _role; QUrl _url; @@ -105,8 +121,11 @@ private: QStringList _maskedJoints; QVector _jointMappings; - + AnimationLoop _animationLoop; + + static QHash, QVector> _jointMappingsCache; + static QVector getJointMappings(const AnimationPointer& animation); }; diff --git a/libraries/render-utils/src/JointState.cpp b/libraries/animation/src/JointState.cpp similarity index 100% rename from libraries/render-utils/src/JointState.cpp rename to libraries/animation/src/JointState.cpp diff --git a/libraries/render-utils/src/JointState.h b/libraries/animation/src/JointState.h similarity index 100% rename from libraries/render-utils/src/JointState.h rename to libraries/animation/src/JointState.h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c8e86319cb..0825a615b7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -9,4 +9,85 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AnimationHandle.h" + #include "Rig.h" + +bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.removeOne(animationHandle); +} + +static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { + for (QList::iterator it = handles.begin(); it != handles.end(); it++) { + if (handle->getPriority() > (*it)->getPriority()) { + handles.insert(it, handle); + return; + } + } + handles.append(handle); +} + +void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { + insertSorted(_runningAnimations, animationHandle); +} + +bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.contains(animationHandle); +} + +void Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states) { + _jointStates = states; + initJointTransforms(scale, offset); + + int numStates = _jointStates.size(); + float radius = 0.0f; + for (int i = 0; i < numStates; ++i) { + float distance = glm::length(_jointStates[i].getPosition()); + if (distance > radius) { + radius = distance; + } + _jointStates[i].buildConstraint(); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + + // XXX update AnimationHandles from here? +} + +void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) { + // compute model transforms + int numStates = _jointStates.size(); + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) + glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset); // * geometry.offset; XXX + state.initTransform(parentTransform); + } else { + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); + } + } +} + +void Rig::resetJoints() { + if (_jointStates.isEmpty()) { + return; + } + + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint(); + _jointStates[i].setRotationInConstrainedFrame(fbxJoint.rotation, 0.0f); + } +} + +AnimationHandlePointer Rig::createAnimationHandle() { + AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); + _animationHandles.insert(handle); + return handle; +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 3e9307385b..eccb0960dd 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -16,8 +16,39 @@ #include -class Rig : public QObject { +#include "JointState.h" +class AnimationHandle; +typedef std::shared_ptr AnimationHandlePointer; +// typedef QWeakPointer WeakAnimationHandlePointer; + +class Rig; +typedef std::shared_ptr RigPointer; + + +class Rig : public QObject, public std::enable_shared_from_this { + +public: + RigPointer getRigPointer() { return shared_from_this(); } + + bool removeRunningAnimation(AnimationHandlePointer animationHandle); + void addRunningAnimation(AnimationHandlePointer animationHandle); + bool isRunningAnimation(AnimationHandlePointer animationHandle); + const QList& getRunningAnimations() const { return _runningAnimations; } + + void initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states); + void initJointTransforms(glm::vec3 scale, glm::vec3 offset); + void resetJoints(); + + QVector getJointStates() { return _jointStates; } + + AnimationHandlePointer createAnimationHandle(); + +protected: + QVector _jointStates; + + QSet _animationHandles; + QList _runningAnimations; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 07df7fbda4..409d24652c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1255,17 +1255,6 @@ QStringList Model::getJointNames() const { return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); } -uint qHash(const WeakAnimationHandlePointer& handle, uint seed) { - return qHash(handle.data(), seed); -} - -AnimationHandlePointer Model::createAnimationHandle() { - AnimationHandlePointer handle(new AnimationHandle(this)); - handle->_self = handle; - _animationHandles.insert(handle); - return handle; -} - // virtual override from PhysicsEntity void Model::buildShapes() { // TODO: figure out how to load/build collision shapes for general models @@ -1830,9 +1819,9 @@ void Model::deleteGeometry() { clearShapes(); for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { - AnimationHandlePointer handle = it->toStrongRef(); + AnimationHandlePointer handle = it->lock(); if (handle) { - handle->_jointMappings.clear(); + handle->clearJoints(); it++; } else { it = _animationHandles.erase(it); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b3a62b8da7..2d9bf7f146 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -209,7 +209,6 @@ public: QStringList getJointNames() const; - AnimationHandlePointer createAnimationHandle(); const QList& getRunningAnimations() const { return _runningAnimations; } From d287817829ef932d4961cc8653fd10bbdece27db Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 17 Jul 2015 14:52:37 -0700 Subject: [PATCH 007/109] give SkeletonModel created by MyAvatar a Rig pointer --- interface/src/avatar/MyAvatar.cpp | 11 ++++++----- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 4 ++-- interface/src/avatar/SkeletonModel.h | 2 +- libraries/render-utils/src/Model.cpp | 13 ++++++++----- libraries/render-utils/src/Model.h | 8 +++++--- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 537b6f339a..896128aee8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -79,7 +79,7 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; MyAvatar::MyAvatar() : - Avatar(), + Avatar(), _gravity(0.0f, 0.0f, 0.0f), _wasPushing(false), _isPushing(false), @@ -102,7 +102,8 @@ MyAvatar::MyAvatar() : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _firstPersonSkeletonModel(this), + _rig(), + _firstPersonSkeletonModel(this, nullptr, _rig), _prevShouldDrawHead(true) { _firstPersonSkeletonModel.setIsFirstPerson(true); @@ -488,7 +489,7 @@ void MyAvatar::loadLastRecording() { } AnimationHandlePointer MyAvatar::addAnimationHandle() { - AnimationHandlePointer handle = _rig.createAnimationHandle(); + AnimationHandlePointer handle = _rig->createAnimationHandle(); _animationHandles.append(handle); return handle; } @@ -506,7 +507,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - AnimationHandlePointer handle = _rig.createAnimationHandle(); + AnimationHandlePointer handle = _rig->createAnimationHandle(); handle->setURL(url); handle->setFPS(fps); handle->setPriority(priority); @@ -534,7 +535,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo } } // no joy; use the parameters provided - AnimationHandlePointer handle = _rig.createAnimationHandle(); + AnimationHandlePointer handle = _rig->createAnimationHandle(); handle->setRole(role); handle->setURL(url); handle->setFPS(fps); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0d94d327a9..8ad2f946ec 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -286,7 +286,7 @@ private: QString _bodyModelName; QString _fullAvatarModelName; - Rig _rig; + RigPointer _rig; // used for rendering when in first person view or when in an HMD. SkeletonModel _firstPersonSkeletonModel; bool _prevShouldDrawHead; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0a981706da..0347e217a3 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -29,8 +29,8 @@ enum StandingFootState { NO_FOOT }; -SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : - Model(parent), +SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : + Model(parent, rig), _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), _boundingShape(), diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 3d63238cf2..6d33b3da7b 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -25,7 +25,7 @@ class SkeletonModel : public Model { public: - SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); + SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); virtual void initJointStates(QVector states); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 409d24652c..7bb96a9082 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -62,7 +62,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; -Model::Model(QObject* parent) : +Model::Model(QObject* parent, RigPointer rig) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), @@ -83,7 +83,8 @@ Model::Model(QObject* parent) : _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), _isWireframe(false), - _renderCollisionHull(false) { + _renderCollisionHull(false), + _rig(rig) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { @@ -1518,8 +1519,9 @@ void Model::updateVisibleJointStates() { } } -bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { +bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, + bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, + float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -1604,7 +1606,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl return true; } -void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { +void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, + const glm::quat& targetRotation, float priority) { // NOTE: targetRotation is from bind- to model-frame if (endIndex == -1 || _jointStates.isEmpty()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 2d9bf7f146..f34690e570 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -67,7 +67,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(QObject* parent = NULL); + Model(QObject* parent = NULL, RigPointer rig = nullptr); virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension @@ -296,8 +296,8 @@ protected: /// \param alignment /// \return true if joint exists bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(), - bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, - const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); + bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, + const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); /// Restores the indexed joint to its default position. /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to @@ -524,6 +524,8 @@ private: QMap _renderItems; bool _readyWhenAdded = false; bool _needsReload = true; + + RigPointer _rig; }; Q_DECLARE_METATYPE(QPointer) From ed815a0573ff67af090bafe55e3736860e532a8c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 17 Jul 2015 16:53:41 -0700 Subject: [PATCH 008/109] Reflect dependencies in unit test setup. --- libraries/animation/src/Rig.h | 2 +- tests/rig/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index eccb0960dd..c3e1edd415 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -16,7 +16,7 @@ #include -#include "JointState.h" +#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS class AnimationHandle; typedef std::shared_ptr AnimationHandlePointer; diff --git a/tests/rig/CMakeLists.txt b/tests/rig/CMakeLists.txt index abf5da12c2..2e9dbc9424 100644 --- a/tests/rig/CMakeLists.txt +++ b/tests/rig/CMakeLists.txt @@ -1,7 +1,7 @@ # Declare dependencies macro (setup_testcase_dependencies) # link in the shared libraries - link_hifi_libraries(shared animation) + link_hifi_libraries(shared animation gpu fbx model) copy_dlls_beside_windows_executable() endmacro () From 9623ccf7929fc716726ba5bd7b8a2a0f4b615768 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 20 Jul 2015 11:07:25 -0700 Subject: [PATCH 009/109] Change instantiation of Rig. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 896128aee8..5433dda1c6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -102,7 +102,7 @@ MyAvatar::MyAvatar() : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _rig(), + _rig(new Rig()), _firstPersonSkeletonModel(this, nullptr, _rig), _prevShouldDrawHead(true) { From abfe60aa20d326484323bb0039c45685aeae428a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 20 Jul 2015 18:31:42 -0700 Subject: [PATCH 010/109] whenever Model class uses _jointState, use the Rig version if there is a Rig. --- interface/src/avatar/SkeletonModel.cpp | 12 +- interface/src/avatar/SkeletonModel.h | 2 +- libraries/animation/src/Rig.cpp | 355 ++++++++++++++++- libraries/animation/src/Rig.h | 27 +- libraries/render-utils/src/Model.cpp | 522 +++++++++++++++---------- libraries/render-utils/src/Model.h | 7 +- 6 files changed, 719 insertions(+), 206 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0347e217a3..3d76b11bac 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -269,21 +269,22 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { } } + void SkeletonModel::updateJointState(int index) { - if (index > _jointStates.size()) { + if (index < 0 && index >= _jointStates.size()) { return; // bail } JointState& state = _jointStates[index]; const FBXJoint& joint = state.getFBXJoint(); - if (joint.parentIndex != -1 && joint.parentIndex <= _jointStates.size()) { + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(joint.parentIndex); const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (index == geometry.leanJointIndex) { maybeUpdateLeanRotation(parentState, state); - + } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint, state); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { maybeUpdateEyeRotation(parentState, joint, state); } @@ -296,6 +297,7 @@ void SkeletonModel::updateJointState(int index) { } } + void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) { if (!_owningAvatar->isMyAvatar()) { return; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 6d33b3da7b..ae8ffe66a9 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -161,7 +161,7 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + Avatar* _owningAvatar; CapsuleShape _boundingShape; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0825a615b7..a76c866140 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -35,7 +35,7 @@ bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } -void Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states) { +float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states) { _jointStates = states; initJointTransforms(scale, offset); @@ -53,6 +53,7 @@ void Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector } // XXX update AnimationHandles from here? + return radius; } void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) { @@ -86,8 +87,360 @@ void Rig::resetJoints() { } } +bool Rig::getJointState(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +bool Rig::getVisibleJointState(int index, glm::quat& rotation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); +} + +void Rig::updateVisibleJointStates() { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } +} + +void Rig::clearJointState(int index) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + state.setRotationInConstrainedFrame(glm::quat(), 0.0f); + } +} + +void Rig::clearJointStates() { + _jointStates.clear(); +} + +void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + } else { + state.restoreRotation(1.0f, priority); + } + } +} + + +void Rig::clearJointAnimationPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = 0.0f; + } +} + AnimationHandlePointer Rig::createAnimationHandle() { AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); _animationHandles.insert(handle); return handle; } + +bool Rig::getJointStateAtIndex(int jointIndex, JointState& jointState) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + jointState = _jointStates[jointIndex]; + return true; +} + +void Rig::updateJointStates(glm::mat4 parentTransform) { + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i, parentTransform); + } +} + +void Rig::updateJointState(int index, glm::mat4 parentTransform) { + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + // glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset) * geometryOffset; + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} + +void Rig::resetAllTransformsChanged() { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); + } +} + +glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInBindFrame(rotation, priority); + endRotation = state.getRotationInBindFrame(); + return endRotation; +} + +void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return; + } + _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); +} + +bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, + bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, + float priority, glm::mat4 parentTransform) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; + const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); + const QVector& freeLineage = fbxJoint.freeLineage; + + + if (freeLineage.isEmpty()) { + return false; + } + if (lastFreeIndex == -1) { + lastFreeIndex = freeLineage.last(); + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + const int ITERATION_COUNT = 1; + glm::vec3 worldAlignment = alignment; + for (int i = 0; i < ITERATION_COUNT; i++) { + // first, try to rotate the end effector as close as possible to the target rotation, if any + glm::quat endRotation; + if (useRotation) { + JointState& state = _jointStates[jointIndex]; + + state.setRotationInBindFrame(rotation, priority); + endRotation = state.getRotationInBindFrame(); + } + + // then, we go from the joint upwards, rotating the end as close as possible to the target + glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); + for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) { + int index = freeLineage.at(j); + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + if (!(joint.isFree || allIntermediatesFree)) { + continue; + } + glm::vec3 jointPosition = extractTranslation(state.getTransform()); + glm::vec3 jointVector = endPosition - jointPosition; + glm::quat oldCombinedRotation = state.getRotation(); + glm::quat combinedDelta; + float combinedWeight; + if (useRotation) { + combinedDelta = safeMix(rotation * glm::inverse(endRotation), + rotationBetween(jointVector, position - jointPosition), 0.5f); + combinedWeight = 2.0f; + + } else { + combinedDelta = rotationBetween(jointVector, position - jointPosition); + combinedWeight = 1.0f; + } + if (alignment != glm::vec3() && j > 1) { + jointVector = endPosition - jointPosition; + glm::vec3 positionSum; + for (int k = j - 1; k > 0; k--) { + int index = freeLineage.at(k); + updateJointState(index, parentTransform); + positionSum += extractTranslation(_jointStates.at(index).getTransform()); + } + glm::vec3 projectedCenterOfMass = glm::cross(jointVector, + glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); + glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); + const float LENGTH_EPSILON = 0.001f; + if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { + combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), + 1.0f / (combinedWeight + 1.0f)); + } + } + state.applyRotationDelta(combinedDelta, true, priority); + glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); + endPosition = actualDelta * jointVector + jointPosition; + if (useRotation) { + endRotation = actualDelta * endRotation; + } + } + } + + // now update the joint states from the top + for (int j = freeLineage.size() - 1; j >= 0; j--) { + updateJointState(freeLineage.at(j), parentTransform); + } + + return true; +} + +void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, + const glm::quat& targetRotation, float priority, glm::mat4 parentTransform) { + // NOTE: targetRotation is from bind- to model-frame + + if (endIndex == -1 || _jointStates.isEmpty()) { + return; + } + + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; + const FBXJoint& fbxJoint = _jointStates[endIndex].getFBXJoint(); + const QVector& freeLineage = fbxJoint.freeLineage; + + if (freeLineage.isEmpty()) { + return; + } + int numFree = freeLineage.size(); + + // store and remember topmost parent transform + glm::mat4 topParentTransform; + { + int index = freeLineage.last(); + const JointState& state = _jointStates.at(index); + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + topParentTransform = parentTransform; + } else { + topParentTransform = _jointStates[parentIndex].getTransform(); + } + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + + // keep track of the position of the end-effector + JointState& endState = _jointStates[endIndex]; + glm::vec3 endPosition = endState.getPosition(); + float distanceToGo = glm::distance(targetPosition, endPosition); + + const int MAX_ITERATION_COUNT = 2; + const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm + int numIterations = 0; + do { + ++numIterations; + // moving up, rotate each free joint to get endPosition closer to target + for (int j = 1; j < numFree; j++) { + int nextIndex = freeLineage.at(j); + JointState& nextState = _jointStates[nextIndex]; + FBXJoint nextJoint = nextState.getFBXJoint(); + if (! nextJoint.isFree) { + continue; + } + + glm::vec3 pivot = nextState.getPosition(); + glm::vec3 leverArm = endPosition - pivot; + float leverLength = glm::length(leverArm); + if (leverLength < EPSILON) { + continue; + } + glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); + + // We want to mix the shortest rotation with one that will pull the system down with gravity + // so that limbs don't float unrealistically. To do this we compute a simplified center of mass + // where each joint has unit mass and we don't bother averaging it because we only need direction. + if (j > 1) { + + glm::vec3 centerOfMass(0.0f); + for (int k = 0; k < j; ++k) { + int massIndex = freeLineage.at(k); + centerOfMass += _jointStates[massIndex].getPosition() - pivot; + } + // the gravitational effect is a rotation that tends to align the two cross products + const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); + glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), + glm::cross(worldAlignment, leverArm)); + + float gravityAngle = glm::angle(gravityDelta); + const float MIN_GRAVITY_ANGLE = 0.1f; + float mixFactor = 0.5f; + if (gravityAngle < MIN_GRAVITY_ANGLE) { + // the final rotation is a mix of the two + mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; + } + deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); + } + + // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose + // in the process. This provides stability to the IK solution for most models. + glm::quat oldNextRotation = nextState.getRotation(); + float mixFactor = 0.03f; + nextState.mixRotationDelta(deltaRotation, mixFactor, priority); + + // measure the result of the rotation which may have been modified by + // blending and constraints + glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); + endPosition = pivot + actualDelta * leverArm; + } + + // recompute transforms from the top down + glm::mat4 parentTransform = topParentTransform; + for (int j = numFree - 1; j >= 0; --j) { + JointState& freeState = _jointStates[freeLineage.at(j)]; + freeState.computeTransform(parentTransform); + parentTransform = freeState.getTransform(); + } + + // measure our success + endPosition = endState.getPosition(); + distanceToGo = glm::distance(targetPosition, endPosition); + } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); + + // set final rotation of the end joint + endState.setRotationInBindFrame(targetRotation, priority, true); +} + +bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return false; + } + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; + const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); + const QVector& freeLineage = fbxJoint.freeLineage; + + foreach (int index, freeLineage) { + JointState& state = _jointStates[index]; + state.restoreRotation(fraction, priority); + } + return true; +} + +float Rig::getLimbLength(int jointIndex, glm::vec3 scale) const { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return 0.0f; + } + + // const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; + const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); + const QVector& freeLineage = fbxJoint.freeLineage; + + float length = 0.0f; + float lengthScale = (scale.x + scale.y + scale.z) / 3.0f; + for (int i = freeLineage.size() - 2; i >= 0; i--) { + int something = freeLineage.at(i); + const FBXJoint& fbxJointI = _jointStates[something].getFBXJoint(); + length += fbxJointI.distanceToParent * lengthScale; + } + return length; +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c3e1edd415..552601f44f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -36,14 +36,39 @@ public: bool isRunningAnimation(AnimationHandlePointer animationHandle); const QList& getRunningAnimations() const { return _runningAnimations; } - void initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states); + float initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states); void initJointTransforms(glm::vec3 scale, glm::vec3 offset); void resetJoints(); + bool jointStatesEmpty() { return _jointStates.isEmpty(); }; + int jointStateCount() const { return _jointStates.size(); } + bool getJointStateAtIndex(int jointIndex, JointState& jointState) const; + + void updateJointStates(glm::mat4 parentTransform); + void updateJointState(int index, glm::mat4 parentTransform); + void resetAllTransformsChanged(); + + bool getJointState(int index, glm::quat& rotation) const; + bool getVisibleJointState(int index, glm::quat& rotation) const; + void updateVisibleJointStates(); + void clearJointState(int index); + void clearJointStates(); + void setJointState(int index, bool valid, const glm::quat& rotation, float priority); + void clearJointAnimationPriority(int index); + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); + void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); QVector getJointStates() { return _jointStates; } AnimationHandlePointer createAnimationHandle(); + bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, + bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, + float priority, glm::mat4 parentTransform); + void inverseKinematics(int endIndex, glm::vec3 targetPosition, + const glm::quat& targetRotation, float priority, glm::mat4 parentTransform); + bool restoreJointPosition(int jointIndex, float fraction, float priority); + float getLimbLength(int jointIndex, glm::vec3 scale) const; + protected: QVector _jointStates; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7bb96a9082..52503a0154 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -85,12 +85,12 @@ Model::Model(QObject* parent, RigPointer rig) : _isWireframe(false), _renderCollisionHull(false), _rig(rig) { - + // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); } - + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } @@ -115,14 +115,14 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); gpu::Shader::makeProgram(*program, slotBindings); - - + + auto locations = std::shared_ptr(new Locations()); initLocations(program, *locations); - + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - + // Backface on shadow if (key.isShadow()) { state->setCullMode(gpu::State::CULL_FRONT); @@ -137,29 +137,30 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, // Blend on transparent state->setBlendFunction(key.isTranslucent(), - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + gpu::State::ONE, gpu::State::BLEND_OP_ADD, + gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); // Good to go add the brand new pipeline auto pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations))); - - + + if (!key.isWireFrame()) { - + RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME); gpu::StatePointer wireframeState = gpu::StatePointer(new gpu::State(state->getValues())); - + wireframeState->setFillMode(gpu::State::FILL_LINE); - + // create a new RenderPipeline with the same shader side and the mirrorState auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); } - + // If not a shadow pass, create the mirror version from the same state, just change the FrontFace if (!key.isShadow()) { - + RenderKey mirrorKey(key.getRaw() | RenderKey::IS_MIRROR); gpu::StatePointer mirrorState = gpu::StatePointer(new gpu::State(state->getValues())); @@ -168,13 +169,13 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, // create a new RenderPipeline with the same shader side and the mirrorState auto mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, mirrorState)); insert(value_type(mirrorKey.getRaw(), RenderPipeline(mirrorPipeline, locations))); - + if (!key.isWireFrame()) { RenderKey wireframeKey(key.getRaw() | RenderKey::IS_MIRROR | RenderKey::IS_WIREFRAME); gpu::StatePointer wireframeState = gpu::StatePointer(new gpu::State(state->getValues()));; - + wireframeState->setFillMode(gpu::State::FILL_LINE); - + // create a new RenderPipeline with the same shader side and the mirrorState auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); @@ -203,7 +204,7 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; locations.clusterWeights = program->getInputs().findLocation("clusterWeights");; - + } @@ -233,12 +234,12 @@ void Model::setScaleInternal(const glm::vec3& scale) { } } -void Model::setOffset(const glm::vec3& offset) { - _offset = offset; - +void Model::setOffset(const glm::vec3& offset) { + _offset = offset; + // if someone manually sets our offset, then we are no longer snapped to center - _snapModelToRegistrationPoint = false; - _snappedToRegistrationPoint = false; + _snapModelToRegistrationPoint = false; + _snappedToRegistrationPoint = false; } QVector Model::createJointStates(const FBXGeometry& geometry) { @@ -255,20 +256,24 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.initTransform(parentTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); + if (_rig) { + _rig->initJointTransforms(_scale, _offset); + } else { + // compute model transforms + int numStates = _jointStates.size(); + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + state.initTransform(parentTransform); + } else { + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); + } } } } @@ -298,7 +303,7 @@ void Model::init() { auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag))); // Fill the renderPipelineLib - + _renderPipelineLib.addRenderPipeline( RenderKey(0), modelVertex, modelPixel); @@ -315,7 +320,7 @@ void Model::init() { RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), modelNormalMapVertex, modelNormalSpecularMapPixel); - + _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::IS_TRANSLUCENT), modelVertex, modelTranslucentPixel); @@ -323,7 +328,7 @@ void Model::init() { _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), modelVertex, modelTranslucentPixel); - + _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), modelNormalMapVertex, modelTranslucentPixel); @@ -399,14 +404,18 @@ void Model::init() { } void Model::reset() { - if (_jointStates.isEmpty()) { - return; + if (_rig) { + _rig->resetJoints(); + } else { + if (_jointStates.isEmpty()) { + return; + } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); + } } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); - } - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -431,6 +440,8 @@ bool Model::updateGeometry() { return false; } + bool jointStatesEmpty = _rig ? _rig->jointStatesEmpty() : _jointStates.isEmpty(); + QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { @@ -439,7 +450,7 @@ bool Model::updateGeometry() { const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector newJointStates = createJointStates(newGeometry); - if (! _jointStates.isEmpty()) { + if (! jointStatesEmpty) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); @@ -447,20 +458,24 @@ bool Model::updateGeometry() { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { - newJointStates[newIndex].copyState(_jointStates[oldIndex]); + JointState jointState; + if (!getJointStateAtIndex(oldIndex, jointState)) { + return false; + } + newJointStates[newIndex].copyState(jointState); } } - } + } deleteGeometry(); _dilatedTextures.clear(); setGeometry(geometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; - } else if (_jointStates.isEmpty()) { + } else if (jointStatesEmpty) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); @@ -472,13 +487,13 @@ bool Model::updateGeometry() { } _geometry->setLoadPriority(this, -_lodDistance); _geometry->ensureLoading(); - + if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); - _meshStates.append(state); + _meshStates.append(state); gpu::BufferPointer buffer(new gpu::Buffer()); if (!mesh.blendshapes.isEmpty()) { @@ -496,25 +511,29 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates(QVector states) { - _jointStates = states; - initJointTransforms(); + if (_rig) { + _boundingRadius = _rig->initJointStates(_scale, _offset, states); + } else { + _jointStates = states; + initJointTransforms(); - int numStates = _jointStates.size(); - float radius = 0.0f; - for (int i = 0; i < numStates; ++i) { - float distance = glm::length(_jointStates[i].getPosition()); - if (distance > radius) { - radius = distance; + int numStates = _jointStates.size(); + float radius = 0.0f; + for (int i = 0; i < numStates; ++i) { + float distance = glm::length(_jointStates[i].getPosition()); + if (distance > radius) { + radius = distance; + } + _jointStates[i].buildConstraint(); } - _jointStates[i].buildConstraint(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + _boundingRadius = radius; } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } - _boundingRadius = radius; } -bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles) { bool intersectedSomething = false; @@ -523,7 +542,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (!isActive()) { return intersectedSomething; } - + // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); @@ -532,7 +551,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); Extents modelExtents = getMeshExtents(); // NOTE: unrotated - + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the ray picking in the model frame of reference AABox modelFrameBox(corner, dimensions); @@ -571,7 +590,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int t = 0; foreach (const Triangle& triangle, meshTriangles) { t++; - + float thisTriangleDistance; if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { if (thisTriangleDistance < bestDistance) { @@ -590,7 +609,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g extraInfo = geometry.getModelNameOfMesh(subMeshIndex); } } - } + } subMeshIndex++; } _mutex.unlock(); @@ -598,7 +617,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (intersectedSomething) { distance = bestDistance; } - + return intersectedSomething; } @@ -610,22 +629,22 @@ bool Model::convexHullContains(glm::vec3 point) { if (!isActive()) { return false; } - + // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); glm::mat4 translation = glm::translate(position); glm::mat4 modelToWorldMatrix = translation * rotation; glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); - + Extents modelExtents = getMeshExtents(); // NOTE: unrotated - + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); AABox modelFrameBox(corner, dimensions); - + glm::vec3 modelFramePoint = glm::vec3(worldToModelMatrix * glm::vec4(point, 1.0f)); - + // we can use the AABox's contains() by mapping our point into the model frame // and testing there. if (modelFrameBox.contains(modelFramePoint)){ @@ -633,7 +652,7 @@ bool Model::convexHullContains(glm::vec3 point) { if (!_calculatedMeshTrianglesValid) { recalculateMeshBoxes(true); } - + // If we are inside the models box, then consider the submeshes... int subMeshIndex = 0; foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { @@ -647,7 +666,7 @@ bool Model::convexHullContains(glm::vec3 point) { insideMesh = false; break; } - + } if (insideMesh) { // It's inside this mesh, return true. @@ -686,7 +705,7 @@ void Model::recalculateMeshPartOffsets() { // Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these // can occur multiple times. In addition, rendering does it's own ray picking in order to decide which // entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible) -// and then the calls use the most recent such result. +// and then the calls use the most recent such result. void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { PROFILE_RANGE(__FUNCTION__); bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; @@ -731,7 +750,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f)); glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f)); glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f)); - + // track the mesh parts in model space if (!atLeastOnePointInBounds) { thisPartBounds.setBox(mv0, 0.0f); @@ -747,18 +766,18 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { glm::vec3 v1 = calculateScaledOffsetPoint(mv1); glm::vec3 v2 = calculateScaledOffsetPoint(mv2); glm::vec3 v3 = calculateScaledOffsetPoint(mv3); - + // Sam's recommended triangle slices Triangle tri1 = { v0, v1, v3 }; Triangle tri2 = { v1, v2, v3 }; - + // NOTE: Random guy on the internet's recommended triangle slices //Triangle tri1 = { v0, v1, v2 }; //Triangle tri2 = { v2, v3, v0 }; - + thisMeshTriangles.push_back(tri1); thisMeshTriangles.push_back(tri2); - + } } @@ -820,7 +839,7 @@ void Model::renderSetup(RenderArgs* args) { _dilatedTextures.append(dilated); } } - + if (!_meshGroupsKnown && isLoaded()) { segregateMeshGroups(); } @@ -833,7 +852,7 @@ public: transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - + bool transparent; Model* model; QUrl url; @@ -842,14 +861,14 @@ public: }; namespace render { - template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { if (!payload->model->isVisible()) { return ItemKey::Builder().withInvisible().build(); } return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape(); } - - template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { + + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); } @@ -903,7 +922,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan _renderItems.insert(item, renderPayload); somethingAdded = true; } - + _readyWhenAdded = readyToAddToScene(); return somethingAdded; @@ -935,7 +954,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan _renderItems.insert(item, renderPayload); somethingAdded = true; } - + _readyWhenAdded = readyToAddToScene(); return somethingAdded; @@ -957,7 +976,7 @@ void Model::renderDebugMeshBoxes() { _debugMeshBoxesID = DependencyManager::get()->allocateID(); } QVector points; - + glm::vec3 brn = box.getCorner(); glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0); glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z); @@ -991,12 +1010,12 @@ void Model::renderDebugMeshBoxes() { { 1.0f, 1.0f, 0.0f, 1.0f }, // yellow { 0.0f, 1.0f, 1.0f, 1.0f }, // cyan { 1.0f, 1.0f, 1.0f, 1.0f }, // white - { 0.0f, 0.5f, 0.0f, 1.0f }, - { 0.0f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.5f, 0.0f, 1.0f }, { 0.0f, 0.5f, 0.5f, 1.0f } }; - + DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]); DependencyManager::get()->renderVertices(gpu::LINES, _debugMeshBoxesID); colorNdx++; @@ -1031,7 +1050,7 @@ Extents Model::getUnscaledMeshExtents() const { if (!isActive()) { return Extents(); } - + const Extents& extents = _geometry->getFBXGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -1039,7 +1058,7 @@ Extents Model::getUnscaledMeshExtents() const { glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; - + return scaledExtents; } @@ -1048,12 +1067,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); - Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), + Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), ((maximum + _offset) * _scale) }; Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); - Extents translatedExtents = { rotatedExtents.minimum + _translation, + Extents translatedExtents = { rotatedExtents.minimum + _translation, rotatedExtents.maximum + _translation }; return translatedExtents; @@ -1075,43 +1094,63 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { bool Model::getJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; + if (_rig) { + return _rig->getJointState(index, rotation); + } else { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); } bool Model::getVisibleJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; + if (_rig) { + return _rig->getVisibleJointState(index, rotation); + } else { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } - const JointState& state = _jointStates.at(index); - rotation = state.getVisibleRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); } void Model::clearJointState(int index) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); + if (_rig) { + _rig->clearJointState(index); + } else { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + state.setRotationInConstrainedFrame(glm::quat(), 0.0f); + } } } void Model::clearJointAnimationPriority(int index) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; + if (_rig) { + _rig->clearJointAnimationPriority(index); + } else { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = 0.0f; + } } } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); + if (_rig) { + _rig->setJointState(index, valid, rotation, priority); + } else { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + } else { + state.restoreRotation(1.0f, priority); + } } } } @@ -1138,7 +1177,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo onInvalidate(); - // if so instructed, keep the current geometry until the new one is loaded + // if so instructed, keep the current geometry until the new one is loaded _nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; if (!retainCurrent || !isActive() || (_nextGeometry && _nextGeometry->isLoaded())) { @@ -1148,14 +1187,14 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo void Model::geometryRefreshed() { QObject* sender = QObject::sender(); - + if (sender == _geometry) { _readyWhenAdded = false; // reset out render items. _needsReload = true; invalidCalculatedMeshBoxes(); - + onInvalidate(); - + // if so instructed, keep the current geometry until the new one is loaded _nextGeometry = DependencyManager::get()->getGeometry(_url); _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; @@ -1175,7 +1214,7 @@ const QSharedPointer Model::getCollisionGeometry(bool delayLoad if (_collisionGeometry && _collisionGeometry->isLoaded()) { return _collisionGeometry; } - + return QSharedPointer(); } @@ -1187,62 +1226,82 @@ void Model::setCollisionModelURL(const QUrl& url) { _collisionGeometry = DependencyManager::get()->getGeometry(url, QUrl(), true); } + +bool Model::getJointStateAtIndex(int jointIndex, JointState& jointState) const { + if (_rig) { + return _rig->getJointStateAtIndex(jointIndex, jointState); + } else { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + jointState = _jointStates[jointIndex]; + return true; + } +} + bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getPosition(); + position = _translation + _rotation * jointState.getPosition(); return true; } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); + position = extractTranslation(jointState.getTransform()); return true; } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } - rotation = _rotation * _jointStates[jointIndex].getRotation(); + rotation = _rotation * jointState.getRotation(); return true; } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } - rotation = _jointStates[jointIndex].getRotation(); + rotation = jointState.getRotation(); return true; } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } - rotation = _rotation * _jointStates[jointIndex].getRotation(); + rotation = _rotation * jointState.getRotation(); return true; } bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); + position = _translation + _rotation * jointState.getVisiblePosition(); return true; } bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + JointState jointState; + if (!getJointStateAtIndex(jointIndex, jointState)) { return false; } - rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); + rotation = _rotation * jointState.getVisibleRotation(); return true; } @@ -1270,11 +1329,11 @@ public: Blender(Model* model, int blendNumber, const QWeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); - + virtual void run(); private: - + QPointer _model; int _blendNumber; QWeakPointer _geometry; @@ -1348,10 +1407,10 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRes } return; } - + if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { _scaleToFit = scaleToFit; - + // we only need to do this work if we're "turning on" scale to fit. if (scaleToFit) { Extents modelMeshExtents = getUnscaledMeshExtents(); @@ -1372,7 +1431,7 @@ void Model::scaleToFit() { // we didn't yet have an active mesh. We can only enter this scaleToFit() in this state // if we now do have an active mesh, so we take this opportunity to actually determine // the correct scale. - if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER + if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER && _scaleToFitDimensions.z == FAKE_DIMENSION_PLACEHOLDER) { setScaleToFit(_scaleToFit, _scaleToFitDimensions.x); } @@ -1407,7 +1466,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); - + if (isActive() && fullUpdate) { // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid // they really only become invalid if something about the transform to world space has changed. This is @@ -1437,12 +1496,20 @@ void Model::updateClusterMatrices() { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + JointState jointState; + if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { + return; + } + state.clusterMatrices[j] = modelToWorld * jointState.getTransform() * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + JointState jointState; + if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { + return; + } + state.clusterMatrices[j] = modelToWorld * jointState.getVisibleTransform() * cluster.inverseBindMatrix; } } } @@ -1450,21 +1517,26 @@ void Model::updateClusterMatrices() { void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints - + // update animations foreach (const AnimationHandlePointer& handle, _runningAnimations) { handle->simulate(deltaTime); } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); + if (_rig) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->updateJointStates(parentTransform); + _rig->resetAllTransformsChanged(); + } else { + updateJointStates(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); + } } _shapesAreDirty = !_shapes.isEmpty(); - + const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { @@ -1473,16 +1545,24 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + JointState jointState; + if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { + return; + } + state.clusterMatrices[j] = modelToWorld * jointState.getTransform() * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + JointState jointState; + if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { + return; + } + state.clusterMatrices[j] = modelToWorld * jointState.getVisibleTransform() * cluster.inverseBindMatrix; } } } - + // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; @@ -1490,10 +1570,18 @@ void Model::simulateInternal(float deltaTime) { } } +void Model::updateJointStates() { + assert(!_rig); + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i); + } +} + void Model::updateJointState(int index) { + assert(!_rig); JointState& state = _jointStates[index]; const FBXJoint& joint = state.getFBXJoint(); - + // compute model transforms int parentIndex = joint.parentIndex; if (parentIndex == -1) { @@ -1514,14 +1602,38 @@ void Model::updateVisibleJointStates() { // no need to update visible transforms return; } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); + if (_rig) { + _rig->updateVisibleJointStates(); + } else { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } } } -bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, - float priority) { +glm::quat Model::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInBindFrame(rotation, priority); + endRotation = state.getRotationInBindFrame(); + return endRotation; +} + + +bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { + if (_rig) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + bool result = _rig->setJointPosition(jointIndex, position, rotation, useRotation, lastFreeIndex, allIntermediatesFree, + alignment, priority, parentTransform); + _shapesAreDirty = !_shapes.isEmpty(); + return result; + } + if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -1609,6 +1721,13 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { // NOTE: targetRotation is from bind- to model-frame + if (_rig) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->inverseKinematics( endIndex, targetPosition, targetRotation, priority, topParentTransform); + _shapesAreDirty = !_shapes.isEmpty(); + return; + } if (endIndex == -1 || _jointStates.isEmpty()) { return; @@ -1718,17 +1837,20 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, // set final rotation of the end joint endState.setRotationInBindFrame(targetRotation, priority, true); - + _shapesAreDirty = !_shapes.isEmpty(); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { + if (_rig) { + return _rig->restoreJointPosition(jointIndex, fraction, priority); + } if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - + foreach (int index, freeLineage) { JointState& state = _jointStates[index]; state.restoreRotation(fraction, priority); @@ -1737,6 +1859,9 @@ bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) } float Model::getLimbLength(int jointIndex) const { + if (_rig) { + return _rig->getLimbLength(jointIndex, _scale); + } if (jointIndex == -1 || _jointStates.isEmpty()) { return 0.0f; } @@ -1770,7 +1895,7 @@ void Model::setBlendedVertices(int blendNumber, const QWeakPointergetFBXGeometry(); + const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -1791,7 +1916,7 @@ void Model::setGeometry(const QSharedPointer& newGeometry) { if (_geometry == newGeometry) { return; } - + if (_geometry) { _geometry->disconnect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); } @@ -1804,10 +1929,10 @@ void Model::applyNextGeometry() { deleteGeometry(); _dilatedTextures.clear(); _lodHysteresis = _nextLODHysteresis; - + // we retain a reference to the base geometry so that its reference count doesn't fall to zero setGeometry(_nextGeometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes _needsReload = false; // we are loaded now! @@ -1818,9 +1943,12 @@ void Model::applyNextGeometry() { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _jointStates.clear(); + if (_rig) { + _rig->clearJointStates(); + } _meshStates.clear(); clearShapes(); - + for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { AnimationHandlePointer handle = it->lock(); if (handle) { @@ -1830,11 +1958,11 @@ void Model::deleteGeometry() { it = _animationHandles.erase(it); } } - + if (_geometry) { _geometry->clearLoadPriority(this); } - + _blendedBlendshapeCoefficients.clear(); } @@ -1848,9 +1976,9 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); } } - + if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { - + // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding // box of the mesh part fails. It seems to create boxes that are not consistent with where the // geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh @@ -1875,7 +2003,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran if (!_readyWhenAdded) { return; // bail asap } - + // We need to make sure we have valid offsets calculated before we can render if (!_calculatedMeshPartOffsetValid) { _mutex.lock(); @@ -1900,13 +2028,13 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // guard against partially loaded meshes if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) { - return; + return; } const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); const FBXMesh& mesh = geometry.meshes.at(meshIndex); const MeshState& state = _meshStates.at(meshIndex); - + bool translucentMesh = translucent; // networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); bool hasSpecular = mesh.hasSpecularTexture(); @@ -1936,7 +2064,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); } #endif //def DEBUG_BOUNDING_PARTS - + if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } @@ -1951,14 +2079,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - + if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) { _meshGroupsKnown = false; // regenerate these lists next time around. _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return; // FIXME! } - + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); int vertexCount = mesh.vertices.size(); if (vertexCount == 0) { @@ -1970,7 +2098,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran if (_transforms.empty()) { _transforms.push_back(Transform()); } - + if (isSkinned) { batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); @@ -2010,7 +2138,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran qCDebug(renderutils) << "WARNING: material == nullptr!!!"; } #endif - + if (material != nullptr) { // apply material properties @@ -2052,12 +2180,12 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran batch._glUniformMatrix4fv(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform); } - if (!mesh.tangents.isEmpty()) { + if (!mesh.tangents.isEmpty()) { NetworkTexture* normalMap = networkPart.normalTexture.data(); batch.setResourceTexture(1, (!normalMap || !normalMap->isLoaded()) ? textureCache->getBlueTexture() : normalMap->getGPUTexture()); } - + if (locations->specularTextureUnit >= 0) { NetworkTexture* specularMap = networkPart.specularTexture.data(); batch.setResourceTexture(locations->specularTextureUnit, (!specularMap || !specularMap->isLoaded()) ? @@ -2075,18 +2203,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran float emissiveOffset = part.emissiveParams.x; float emissiveScale = part.emissiveParams.y; batch._glUniform2f(locations->emissiveParams, emissiveOffset, emissiveScale); - + NetworkTexture* emissiveMap = networkPart.emissiveTexture.data(); batch.setResourceTexture(locations->emissiveTextureUnit, (!emissiveMap || !emissiveMap->isLoaded()) ? textureCache->getGrayTexture() : emissiveMap->getGPUTexture()); } - + if (translucent && locations->lightBufferUnit >= 0) { DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); } } } - + qint64 offset; { // FIXME_STUTTER: We should n't have any lock here @@ -2123,7 +2251,7 @@ void Model::segregateMeshGroups() { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } - + _transparentRenderItems.clear(); _opaqueRenderItems.clear(); @@ -2132,7 +2260,7 @@ void Model::segregateMeshGroups() { const NetworkMesh& networkMesh = networkMeshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i); const MeshState& state = _meshStates.at(i); - + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); @@ -2140,7 +2268,7 @@ void Model::segregateMeshGroups() { bool hasLightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); - + if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } @@ -2157,7 +2285,7 @@ void Model::segregateMeshGroups() { } } _meshGroupsKnown = true; -} +} void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, @@ -2177,7 +2305,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram(); locations = (*pipeline).second._locations.get(); - + // Setup the One pipeline batch.setPipeline((*pipeline).second._pipeline); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f34690e570..95c191732a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -262,6 +262,7 @@ protected: bool _showTrueJointTransforms; + bool getJointStateAtIndex(int jointIndex, JointState& jointState) const; QVector _jointStates; class MeshState { @@ -283,10 +284,13 @@ protected: void simulateInternal(float deltaTime); /// Updates the state of the joint at the specified index. + void updateJointStates(); virtual void updateJointState(int index); virtual void updateVisibleJointStates(); - + + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); + /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame @@ -525,6 +529,7 @@ private: bool _readyWhenAdded = false; bool _needsReload = true; +protected: RigPointer _rig; }; From a161f527c4071e2cd3bbfda23d0bf8ae57a05ea8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 22 Jul 2015 11:41:24 -0700 Subject: [PATCH 011/109] Comments documenting direction and questions. --- libraries/animation/src/Rig.h | 8 ++++++++ tests/rig/src/RigTests.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c3e1edd415..99a83a2a8d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -10,6 +10,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* TBD: + - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? + - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics? + - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly? + + - Distribute some doc from here to the right files if it turns out to be correct: + - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity. + */ #ifndef __hifi__Rig__ #define __hifi__Rig__ diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index c32e136fd1..ca07cda844 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -8,6 +8,36 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* FIXME/TBD: + + The following lower level functionality might be separated out into a separate class, covered by a separate test case class: + - With no input, initial pose is standing, arms at side + - Some single animation produces correct results at a given keyframe time. + - Some single animation produces correct results at a given interpolated time. + - Blend between two animations, started at separate times, produces correct result at a given interpolated time. + - Head orientation can be overridden to produce change that doesn't come from the playing animation. + - Hand position/orientation can be overridden to produce change that doesn't come from the playing animation. + - Hand position/orientation can be overrridden to produce elbow change that doesn't come from the playing animation. + - Respect scaling? (e.g., so that MyAvatar.increase/decreaseSize can alter rig, such that anti-scating and footfalls-on-stairs works) + + Higher level functionality: + - start/stopAnimation adds the animation to that which is playing, blending/fading as needed. + - thrust causes walk role animation to be used. + - turning causes turn role animation to be used. (two tests, correctly symmetric left & right) + - walk/turn do not skate (footfalls match over-ground velocity) + - (Later?) walk up stairs / hills have proper footfall for terrain + - absence of above causes return to idle role animation to be used + - (later?) The lower-level head/hand placements respect previous state. E.g., actual hand movement can be slower than requested. + - (later) The lower-level head/hand placements can move whole skeleton. E.g., turning head past a limit may turn whole body. Reaching up can move shoulders and hips. + + Backward-compatability operations. We should think of this behavior as deprecated: + - clearJointData return to standing. TBD: presumably with idle and all other animations NOT playing, until explicitly reenabled with a new TBD method? + - setJointData applies the given data. Same TBD. + These can be defined true or false, but the tests document the behavior and tells us if something's changed: + - An external change to the original skeleton IS/ISN'T seen by the rig. + - An external change to the original skeleton's head orientation IS/ISN'T seen by the rig. + - An external change to the original skeleton's hand orientiation IS/ISN'T seen by the rig. + */ #include #include "RigTests.h" From 7c8d52cbd15f3f5b2f052703aeeac1c8e9108466 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 22 Jul 2015 13:41:31 -0700 Subject: [PATCH 012/109] back out some changes to Model.cpp, change how rig pointer is delivered to model initilizer --- interface/src/avatar/Avatar.cpp | 4 +- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/AvatarManager.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 8 +- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 9 +- interface/src/avatar/SkeletonModel.h | 2 +- libraries/animation/src/Rig.cpp | 15 +- libraries/animation/src/Rig.h | 4 +- libraries/render-utils/src/Model.cpp | 521 +++++++++---------------- libraries/render-utils/src/Model.h | 13 +- tools/vhacd-util/src/VHACDUtil.cpp | 2 + 12 files changed, 228 insertions(+), 358 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3a55e73fa6..1d14a0f464 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -75,9 +75,9 @@ namespace render { } } -Avatar::Avatar() : +Avatar::Avatar(RigPointer rig) : AvatarData(), - _skeletonModel(this), + _skeletonModel(this, nullptr, rig), _skeletonOffset(0.0f), _bodyYawDelta(0.0f), _positionDeltaAccumulator(0.0f), diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index ae99317541..096af5c229 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -72,7 +72,7 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: - Avatar(); + Avatar(RigPointer rig = nullptr); ~Avatar(); typedef render::Payload Payload; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d5922366e9..730c77e9c3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -65,7 +65,7 @@ AvatarManager::AvatarManager(QObject* parent) : { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::make_shared(); + _myAvatar = std::make_shared(std::make_shared()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -160,7 +160,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared()); + return AvatarSharedPointer(std::make_shared(std::make_shared())); } // virtual diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c59f92f33f..b59d605752 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -78,8 +78,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -MyAvatar::MyAvatar() : - Avatar(), +MyAvatar::MyAvatar(RigPointer rig) : + Avatar(rig), _gravity(0.0f, 0.0f, 0.0f), _wasPushing(false), _isPushing(false), @@ -102,8 +102,8 @@ MyAvatar::MyAvatar() : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _rig(new Rig()), - _firstPersonSkeletonModel(this, nullptr, _rig), + _rig(rig), + _firstPersonSkeletonModel(this, nullptr, rig), _prevShouldDrawHead(true) { _firstPersonSkeletonModel.setIsFirstPerson(true); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d2c3ee9ab0..ba84ab0e96 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -36,7 +36,7 @@ class MyAvatar : public Avatar { //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) public: - MyAvatar(); + MyAvatar(RigPointer rig); ~MyAvatar(); QByteArray toByteArray(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 551d616732..c8e6f03d69 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -42,6 +42,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r _headClipDistance(DEFAULT_NEAR_CLIP), _isFirstPerson(false) { + assert(_rig); assert(_owningAvatar); _enableShapes = true; } @@ -269,7 +270,6 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { } } - void SkeletonModel::updateJointState(int index) { if (index < 0 && index >= _jointStates.size()) { return; // bail @@ -281,10 +281,10 @@ void SkeletonModel::updateJointState(int index) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (index == geometry.leanJointIndex) { maybeUpdateLeanRotation(parentState, state); - + } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint, state); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { maybeUpdateEyeRotation(parentState, joint, state); } @@ -297,7 +297,6 @@ void SkeletonModel::updateJointState(int index) { } } - void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) { if (!_owningAvatar->isMyAvatar()) { return; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index ae8ffe66a9..6d33b3da7b 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -161,7 +161,7 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + Avatar* _owningAvatar; CapsuleShape _boundingShape; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a76c866140..5e322e420f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -184,17 +184,28 @@ void Rig::resetAllTransformsChanged() { } } -glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { +glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { glm::quat endRotation; if (jointIndex == -1 || _jointStates.isEmpty()) { return endRotation; } JointState& state = _jointStates[jointIndex]; - state.setRotationInBindFrame(rotation, priority); + state.setRotationInBindFrame(rotation, priority, constrain); endRotation = state.getRotationInBindFrame(); return endRotation; } +glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInConstrainedFrame(targetRotation, priority, constrain); + endRotation = state.getRotationInConstrainedFrame(); + return endRotation; +} + void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 552601f44f..b1155cbb9d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -54,7 +54,9 @@ public: void clearJointStates(); void setJointState(int index, bool valid, const glm::quat& rotation, float priority); void clearJointAnimationPriority(int index); - glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false); + glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, + float priority, bool constrain = false); void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); QVector getJointStates() { return _jointStates; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index df805e4d29..5a6908fc4a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -83,12 +83,11 @@ Model::Model(QObject* parent, RigPointer rig) : _isWireframe(false), _renderCollisionHull(false), _rig(rig) { - // we may have been created in the network thread, but we live in the main thread if (_viewState) { moveToThread(_viewState->getMainThread()); } - + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } @@ -113,12 +112,14 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); gpu::Shader::makeProgram(*program, slotBindings); - + + auto locations = std::make_shared(); initLocations(program, *locations); + auto state = std::make_shared(); - + // Backface on shadow if (key.isShadow()) { state->setCullMode(gpu::State::CULL_FRONT); @@ -133,35 +134,29 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, // Blend on transparent state->setBlendFunction(key.isTranslucent(), - gpu::State::ONE, gpu::State::BLEND_OP_ADD, - gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); // Good to go add the brand new pipeline auto pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations))); - - + + if (!key.isWireFrame()) { - + RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME); -<<<<<<< HEAD - gpu::StatePointer wireframeState = gpu::StatePointer(new gpu::State(state->getValues())); - -======= auto wireframeState = std::make_shared(state->getValues()); ->>>>>>> cf88bce0820df76a55473370db0fcb8c5fe6763d wireframeState->setFillMode(gpu::State::FILL_LINE); - + // create a new RenderPipeline with the same shader side and the mirrorState auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); } - + // If not a shadow pass, create the mirror version from the same state, just change the FrontFace if (!key.isShadow()) { - + RenderKey mirrorKey(key.getRaw() | RenderKey::IS_MIRROR); auto mirrorState = std::make_shared(state->getValues()); @@ -170,18 +165,13 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, // create a new RenderPipeline with the same shader side and the mirrorState auto mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, mirrorState)); insert(value_type(mirrorKey.getRaw(), RenderPipeline(mirrorPipeline, locations))); - + if (!key.isWireFrame()) { RenderKey wireframeKey(key.getRaw() | RenderKey::IS_MIRROR | RenderKey::IS_WIREFRAME); -<<<<<<< HEAD - gpu::StatePointer wireframeState = gpu::StatePointer(new gpu::State(state->getValues()));; - -======= auto wireframeState = std::make_shared(state->getValues()); ->>>>>>> cf88bce0820df76a55473370db0fcb8c5fe6763d wireframeState->setFillMode(gpu::State::FILL_LINE); - + // create a new RenderPipeline with the same shader side and the mirrorState auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); @@ -210,7 +200,7 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; locations.clusterWeights = program->getInputs().findLocation("clusterWeights");; - + } @@ -240,12 +230,12 @@ void Model::setScaleInternal(const glm::vec3& scale) { } } -void Model::setOffset(const glm::vec3& offset) { - _offset = offset; - +void Model::setOffset(const glm::vec3& offset) { + _offset = offset; + // if someone manually sets our offset, then we are no longer snapped to center - _snapModelToRegistrationPoint = false; - _snappedToRegistrationPoint = false; + _snapModelToRegistrationPoint = false; + _snappedToRegistrationPoint = false; } QVector Model::createJointStates(const FBXGeometry& geometry) { @@ -262,24 +252,20 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - if (_rig) { - _rig->initJointTransforms(_scale, _offset); - } else { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.initTransform(parentTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } + // compute model transforms + int numStates = _jointStates.size(); + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + state.initTransform(parentTransform); + } else { + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); } } } @@ -309,7 +295,7 @@ void Model::init() { auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag))); // Fill the renderPipelineLib - + _renderPipelineLib.addRenderPipeline( RenderKey(0), modelVertex, modelPixel); @@ -326,7 +312,7 @@ void Model::init() { RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), modelNormalMapVertex, modelNormalSpecularMapPixel); - + _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::IS_TRANSLUCENT), modelVertex, modelTranslucentPixel); @@ -334,7 +320,7 @@ void Model::init() { _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), modelVertex, modelTranslucentPixel); - + _renderPipelineLib.addRenderPipeline( RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), modelNormalMapVertex, modelTranslucentPixel); @@ -410,18 +396,14 @@ void Model::init() { } void Model::reset() { - if (_rig) { - _rig->resetJoints(); - } else { - if (_jointStates.isEmpty()) { - return; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); - } + if (_jointStates.isEmpty()) { + return; } - + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); + } + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -446,8 +428,6 @@ bool Model::updateGeometry() { return false; } - bool jointStatesEmpty = _rig ? _rig->jointStatesEmpty() : _jointStates.isEmpty(); - QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); if (_geometry != geometry) { @@ -456,7 +436,7 @@ bool Model::updateGeometry() { const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector newJointStates = createJointStates(newGeometry); - if (! jointStatesEmpty) { + if (! _jointStates.isEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); @@ -464,24 +444,20 @@ bool Model::updateGeometry() { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { - JointState jointState; - if (!getJointStateAtIndex(oldIndex, jointState)) { - return false; - } - newJointStates[newIndex].copyState(jointState); + newJointStates[newIndex].copyState(_jointStates[oldIndex]); } } - } + } deleteGeometry(); _dilatedTextures.clear(); setGeometry(geometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; - } else if (jointStatesEmpty) { + } else if (_jointStates.isEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); @@ -493,21 +469,15 @@ bool Model::updateGeometry() { } _geometry->setLoadPriority(this, -_lodDistance); _geometry->ensureLoading(); - + if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); -<<<<<<< HEAD - _meshStates.append(state); - - gpu::BufferPointer buffer(new gpu::Buffer()); -======= _meshStates.append(state); auto buffer = std::make_shared(); ->>>>>>> cf88bce0820df76a55473370db0fcb8c5fe6763d if (!mesh.blendshapes.isEmpty()) { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); @@ -523,29 +493,25 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates(QVector states) { - if (_rig) { - _boundingRadius = _rig->initJointStates(_scale, _offset, states); - } else { - _jointStates = states; - initJointTransforms(); + _jointStates = states; + initJointTransforms(); - int numStates = _jointStates.size(); - float radius = 0.0f; - for (int i = 0; i < numStates; ++i) { - float distance = glm::length(_jointStates[i].getPosition()); - if (distance > radius) { - radius = distance; - } - _jointStates[i].buildConstraint(); + int numStates = _jointStates.size(); + float radius = 0.0f; + for (int i = 0; i < numStates; ++i) { + float distance = glm::length(_jointStates[i].getPosition()); + if (distance > radius) { + radius = distance; } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } - _boundingRadius = radius; + _jointStates[i].buildConstraint(); } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + _boundingRadius = radius; } -bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles) { bool intersectedSomething = false; @@ -554,7 +520,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (!isActive()) { return intersectedSomething; } - + // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); @@ -563,7 +529,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); Extents modelExtents = getMeshExtents(); // NOTE: unrotated - + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the ray picking in the model frame of reference AABox modelFrameBox(corner, dimensions); @@ -602,7 +568,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int t = 0; foreach (const Triangle& triangle, meshTriangles) { t++; - + float thisTriangleDistance; if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { if (thisTriangleDistance < bestDistance) { @@ -621,7 +587,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g extraInfo = geometry.getModelNameOfMesh(subMeshIndex); } } - } + } subMeshIndex++; } _mutex.unlock(); @@ -629,7 +595,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (intersectedSomething) { distance = bestDistance; } - + return intersectedSomething; } @@ -641,22 +607,22 @@ bool Model::convexHullContains(glm::vec3 point) { if (!isActive()) { return false; } - + // extents is the entity relative, scaled, centered extents of the entity glm::vec3 position = _translation; glm::mat4 rotation = glm::mat4_cast(_rotation); glm::mat4 translation = glm::translate(position); glm::mat4 modelToWorldMatrix = translation * rotation; glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); - + Extents modelExtents = getMeshExtents(); // NOTE: unrotated - + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; glm::vec3 corner = -(dimensions * _registrationPoint); AABox modelFrameBox(corner, dimensions); - + glm::vec3 modelFramePoint = glm::vec3(worldToModelMatrix * glm::vec4(point, 1.0f)); - + // we can use the AABox's contains() by mapping our point into the model frame // and testing there. if (modelFrameBox.contains(modelFramePoint)){ @@ -664,7 +630,7 @@ bool Model::convexHullContains(glm::vec3 point) { if (!_calculatedMeshTrianglesValid) { recalculateMeshBoxes(true); } - + // If we are inside the models box, then consider the submeshes... int subMeshIndex = 0; foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { @@ -678,7 +644,7 @@ bool Model::convexHullContains(glm::vec3 point) { insideMesh = false; break; } - + } if (insideMesh) { // It's inside this mesh, return true. @@ -717,7 +683,7 @@ void Model::recalculateMeshPartOffsets() { // Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these // can occur multiple times. In addition, rendering does it's own ray picking in order to decide which // entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible) -// and then the calls use the most recent such result. +// and then the calls use the most recent such result. void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { PROFILE_RANGE(__FUNCTION__); bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; @@ -762,7 +728,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f)); glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f)); glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f)); - + // track the mesh parts in model space if (!atLeastOnePointInBounds) { thisPartBounds.setBox(mv0, 0.0f); @@ -778,18 +744,18 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { glm::vec3 v1 = calculateScaledOffsetPoint(mv1); glm::vec3 v2 = calculateScaledOffsetPoint(mv2); glm::vec3 v3 = calculateScaledOffsetPoint(mv3); - + // Sam's recommended triangle slices Triangle tri1 = { v0, v1, v3 }; Triangle tri2 = { v1, v2, v3 }; - + // NOTE: Random guy on the internet's recommended triangle slices //Triangle tri1 = { v0, v1, v2 }; //Triangle tri2 = { v2, v3, v0 }; - + thisMeshTriangles.push_back(tri1); thisMeshTriangles.push_back(tri2); - + } } @@ -851,7 +817,7 @@ void Model::renderSetup(RenderArgs* args) { _dilatedTextures.append(dilated); } } - + if (!_meshGroupsKnown && isLoaded()) { segregateMeshGroups(); } @@ -864,7 +830,7 @@ public: transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - + bool transparent; Model* model; QUrl url; @@ -873,14 +839,14 @@ public: }; namespace render { - template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { if (!payload->model->isVisible()) { return ItemKey::Builder().withInvisible().build(); } return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape(); } - - template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { + + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { if (payload) { return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); } @@ -934,7 +900,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan _renderItems.insert(item, renderPayload); somethingAdded = true; } - + _readyWhenAdded = readyToAddToScene(); return somethingAdded; @@ -966,7 +932,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan _renderItems.insert(item, renderPayload); somethingAdded = true; } - + _readyWhenAdded = readyToAddToScene(); return somethingAdded; @@ -988,7 +954,7 @@ void Model::renderDebugMeshBoxes() { _debugMeshBoxesID = DependencyManager::get()->allocateID(); } QVector points; - + glm::vec3 brn = box.getCorner(); glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0); glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z); @@ -1022,12 +988,12 @@ void Model::renderDebugMeshBoxes() { { 1.0f, 1.0f, 0.0f, 1.0f }, // yellow { 0.0f, 1.0f, 1.0f, 1.0f }, // cyan { 1.0f, 1.0f, 1.0f, 1.0f }, // white - { 0.0f, 0.5f, 0.0f, 1.0f }, - { 0.0f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.0f, 0.5f, 1.0f }, - { 0.5f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.5f, 0.0f, 1.0f }, { 0.0f, 0.5f, 0.5f, 1.0f } }; - + DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]); DependencyManager::get()->renderVertices(gpu::LINES, _debugMeshBoxesID); colorNdx++; @@ -1062,7 +1028,7 @@ Extents Model::getUnscaledMeshExtents() const { if (!isActive()) { return Extents(); } - + const Extents& extents = _geometry->getFBXGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -1070,7 +1036,7 @@ Extents Model::getUnscaledMeshExtents() const { glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; - + return scaledExtents; } @@ -1079,12 +1045,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); - Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), + Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), ((maximum + _offset) * _scale) }; Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); - Extents translatedExtents = { rotatedExtents.minimum + _translation, + Extents translatedExtents = { rotatedExtents.minimum + _translation, rotatedExtents.maximum + _translation }; return translatedExtents; @@ -1106,39 +1072,27 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { bool Model::getJointState(int index, glm::quat& rotation) const { - if (_rig) { - return _rig->getJointState(index, rotation); - } else { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + if (index == -1 || index >= _jointStates.size()) { + return false; } + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } bool Model::getVisibleJointState(int index, glm::quat& rotation) const { - if (_rig) { - return _rig->getVisibleJointState(index, rotation); - } else { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getVisibleRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + if (index == -1 || index >= _jointStates.size()) { + return false; } + const JointState& state = _jointStates.at(index); + rotation = state.getVisibleRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); } void Model::clearJointState(int index) { - if (_rig) { - _rig->clearJointState(index); - } else { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); - } + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + state.setRotationInConstrainedFrame(glm::quat(), 0.0f); } } @@ -1153,16 +1107,12 @@ void Model::clearJointAnimationPriority(int index) { } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - if (_rig) { - _rig->setJointState(index, valid, rotation, priority); - } else { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); - } + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + } else { + state.restoreRotation(1.0f, priority); } } } @@ -1189,7 +1139,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo onInvalidate(); - // if so instructed, keep the current geometry until the new one is loaded + // if so instructed, keep the current geometry until the new one is loaded _nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; if (!retainCurrent || !isActive() || (_nextGeometry && _nextGeometry->isLoaded())) { @@ -1199,14 +1149,14 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo void Model::geometryRefreshed() { QObject* sender = QObject::sender(); - + if (sender == _geometry) { _readyWhenAdded = false; // reset out render items. _needsReload = true; invalidCalculatedMeshBoxes(); - + onInvalidate(); - + // if so instructed, keep the current geometry until the new one is loaded _nextGeometry = DependencyManager::get()->getGeometry(_url); _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; @@ -1226,7 +1176,7 @@ const QSharedPointer Model::getCollisionGeometry(bool delayLoad if (_collisionGeometry && _collisionGeometry->isLoaded()) { return _collisionGeometry; } - + return QSharedPointer(); } @@ -1238,82 +1188,62 @@ void Model::setCollisionModelURL(const QUrl& url) { _collisionGeometry = DependencyManager::get()->getGeometry(url, QUrl(), true); } - -bool Model::getJointStateAtIndex(int jointIndex, JointState& jointState) const { - if (_rig) { - return _rig->getJointStateAtIndex(jointIndex, jointState); - } else { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - jointState = _jointStates[jointIndex]; - return true; - } -} - bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } // position is in world-frame - position = _translation + _rotation * jointState.getPosition(); + position = _translation + _rotation * _jointStates[jointIndex].getPosition(); return true; } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } // position is in model-frame - position = extractTranslation(jointState.getTransform()); + position = extractTranslation(_jointStates[jointIndex].getTransform()); return true; } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = _rotation * jointState.getRotation(); + rotation = _rotation * _jointStates[jointIndex].getRotation(); return true; } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = jointState.getRotation(); + rotation = _jointStates[jointIndex].getRotation(); return true; } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = _rotation * jointState.getRotation(); + rotation = _rotation * _jointStates[jointIndex].getRotation(); return true; } bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } // position is in world-frame - position = _translation + _rotation * jointState.getVisiblePosition(); + position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); return true; } bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - JointState jointState; - if (!getJointStateAtIndex(jointIndex, jointState)) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = _rotation * jointState.getVisibleRotation(); + rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); return true; } @@ -1341,11 +1271,11 @@ public: Blender(Model* model, int blendNumber, const QWeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); - + virtual void run(); private: - + QPointer _model; int _blendNumber; QWeakPointer _geometry; @@ -1419,10 +1349,10 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRes } return; } - + if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { _scaleToFit = scaleToFit; - + // we only need to do this work if we're "turning on" scale to fit. if (scaleToFit) { Extents modelMeshExtents = getUnscaledMeshExtents(); @@ -1443,7 +1373,7 @@ void Model::scaleToFit() { // we didn't yet have an active mesh. We can only enter this scaleToFit() in this state // if we now do have an active mesh, so we take this opportunity to actually determine // the correct scale. - if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER + if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER && _scaleToFitDimensions.z == FAKE_DIMENSION_PLACEHOLDER) { setScaleToFit(_scaleToFit, _scaleToFitDimensions.x); } @@ -1478,7 +1408,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); - + if (isActive() && fullUpdate) { // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid // they really only become invalid if something about the transform to world space has changed. This is @@ -1508,20 +1438,12 @@ void Model::updateClusterMatrices() { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - JointState jointState; - if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { - return; - } - state.clusterMatrices[j] = modelToWorld * jointState.getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - JointState jointState; - if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { - return; - } - state.clusterMatrices[j] = modelToWorld * jointState.getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; } } } @@ -1529,26 +1451,21 @@ void Model::updateClusterMatrices() { void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints - + // update animations foreach (const AnimationHandlePointer& handle, _runningAnimations) { handle->simulate(deltaTime); } - if (_rig) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->updateJointStates(parentTransform); - _rig->resetAllTransformsChanged(); - } else { - updateJointStates(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i); + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); } _shapesAreDirty = !_shapes.isEmpty(); - + const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { @@ -1557,24 +1474,16 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - JointState jointState; - if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { - return; - } - state.clusterMatrices[j] = modelToWorld * jointState.getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - JointState jointState; - if (!getJointStateAtIndex(cluster.jointIndex, jointState)) { - return; - } - state.clusterMatrices[j] = modelToWorld * jointState.getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; } } } - + // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; @@ -1582,18 +1491,10 @@ void Model::simulateInternal(float deltaTime) { } } -void Model::updateJointStates() { - assert(!_rig); - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); - } -} - void Model::updateJointState(int index) { - assert(!_rig); JointState& state = _jointStates[index]; const FBXJoint& joint = state.getFBXJoint(); - + // compute model transforms int parentIndex = joint.parentIndex; if (parentIndex == -1) { @@ -1614,38 +1515,13 @@ void Model::updateVisibleJointStates() { // no need to update visible transforms return; } - if (_rig) { - _rig->updateVisibleJointStates(); - } else { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); } } -glm::quat Model::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - return endRotation; -} - - bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { - if (_rig) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - bool result = _rig->setJointPosition(jointIndex, position, rotation, useRotation, lastFreeIndex, allIntermediatesFree, - alignment, priority, parentTransform); - _shapesAreDirty = !_shapes.isEmpty(); - return result; - } - + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -1730,16 +1606,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl return true; } -void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, - const glm::quat& targetRotation, float priority) { +void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { // NOTE: targetRotation is from bind- to model-frame - if (_rig) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->inverseKinematics( endIndex, targetPosition, targetRotation, priority, topParentTransform); - _shapesAreDirty = !_shapes.isEmpty(); - return; - } if (endIndex == -1 || _jointStates.isEmpty()) { return; @@ -1849,20 +1717,17 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, // set final rotation of the end joint endState.setRotationInBindFrame(targetRotation, priority, true); - + _shapesAreDirty = !_shapes.isEmpty(); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - if (_rig) { - return _rig->restoreJointPosition(jointIndex, fraction, priority); - } if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - + foreach (int index, freeLineage) { JointState& state = _jointStates[index]; state.restoreRotation(fraction, priority); @@ -1871,9 +1736,6 @@ bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) } float Model::getLimbLength(int jointIndex) const { - if (_rig) { - return _rig->getLimbLength(jointIndex, _scale); - } if (jointIndex == -1 || _jointStates.isEmpty()) { return 0.0f; } @@ -1907,7 +1769,7 @@ void Model::setBlendedVertices(int blendNumber, const QWeakPointergetFBXGeometry(); + const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -1928,7 +1790,7 @@ void Model::setGeometry(const QSharedPointer& newGeometry) { if (_geometry == newGeometry) { return; } - + if (_geometry) { _geometry->disconnect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); } @@ -1941,10 +1803,10 @@ void Model::applyNextGeometry() { deleteGeometry(); _dilatedTextures.clear(); _lodHysteresis = _nextLODHysteresis; - + // we retain a reference to the base geometry so that its reference count doesn't fall to zero setGeometry(_nextGeometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes _needsReload = false; // we are loaded now! @@ -1955,12 +1817,9 @@ void Model::applyNextGeometry() { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _jointStates.clear(); - if (_rig) { - _rig->clearJointStates(); - } _meshStates.clear(); clearShapes(); - + for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { AnimationHandlePointer handle = it->lock(); if (handle) { @@ -1970,11 +1829,11 @@ void Model::deleteGeometry() { it = _animationHandles.erase(it); } } - + if (_geometry) { _geometry->clearLoadPriority(this); } - + _blendedBlendshapeCoefficients.clear(); } @@ -1988,9 +1847,9 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); } } - + if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { - + // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding // box of the mesh part fails. It seems to create boxes that are not consistent with where the // geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh @@ -2015,7 +1874,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran if (!_readyWhenAdded) { return; // bail asap } - + // We need to make sure we have valid offsets calculated before we can render if (!_calculatedMeshPartOffsetValid) { _mutex.lock(); @@ -2040,13 +1899,13 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // guard against partially loaded meshes if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) { - return; + return; } const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); const FBXMesh& mesh = geometry.meshes.at(meshIndex); const MeshState& state = _meshStates.at(meshIndex); - + bool translucentMesh = translucent; // networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); bool hasSpecular = mesh.hasSpecularTexture(); @@ -2076,7 +1935,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); } #endif //def DEBUG_BOUNDING_PARTS - + if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } @@ -2091,14 +1950,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - + if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) { _meshGroupsKnown = false; // regenerate these lists next time around. _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return; // FIXME! } - + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); int vertexCount = mesh.vertices.size(); if (vertexCount == 0) { @@ -2110,7 +1969,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran if (_transforms.empty()) { _transforms.push_back(Transform()); } - + if (isSkinned) { batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); @@ -2150,7 +2009,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran qCDebug(renderutils) << "WARNING: material == nullptr!!!"; } #endif - + if (material != nullptr) { // apply material properties @@ -2192,12 +2051,12 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran batch._glUniformMatrix4fv(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform); } - if (!mesh.tangents.isEmpty()) { + if (!mesh.tangents.isEmpty()) { NetworkTexture* normalMap = networkPart.normalTexture.data(); batch.setResourceTexture(1, (!normalMap || !normalMap->isLoaded()) ? textureCache->getBlueTexture() : normalMap->getGPUTexture()); } - + if (locations->specularTextureUnit >= 0) { NetworkTexture* specularMap = networkPart.specularTexture.data(); batch.setResourceTexture(locations->specularTextureUnit, (!specularMap || !specularMap->isLoaded()) ? @@ -2215,18 +2074,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran float emissiveOffset = part.emissiveParams.x; float emissiveScale = part.emissiveParams.y; batch._glUniform2f(locations->emissiveParams, emissiveOffset, emissiveScale); - + NetworkTexture* emissiveMap = networkPart.emissiveTexture.data(); batch.setResourceTexture(locations->emissiveTextureUnit, (!emissiveMap || !emissiveMap->isLoaded()) ? textureCache->getGrayTexture() : emissiveMap->getGPUTexture()); } - + if (translucent && locations->lightBufferUnit >= 0) { DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); } } } - + qint64 offset; { // FIXME_STUTTER: We should n't have any lock here @@ -2263,7 +2122,7 @@ void Model::segregateMeshGroups() { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } - + _transparentRenderItems.clear(); _opaqueRenderItems.clear(); @@ -2272,7 +2131,7 @@ void Model::segregateMeshGroups() { const NetworkMesh& networkMesh = networkMeshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i); const MeshState& state = _meshStates.at(i); - + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); @@ -2280,7 +2139,7 @@ void Model::segregateMeshGroups() { bool hasLightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); - + if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } @@ -2297,7 +2156,7 @@ void Model::segregateMeshGroups() { } } _meshGroupsKnown = true; -} +} void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, @@ -2317,7 +2176,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram(); locations = (*pipeline).second._locations.get(); - + // Setup the One pipeline batch.setPipeline((*pipeline).second._pipeline); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5289302390..1b4df6821c 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,7 +64,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(QObject* parent = NULL, RigPointer rig = nullptr); + Model(QObject* parent = nullptr, RigPointer rig = nullptr); virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension @@ -206,6 +206,7 @@ public: QStringList getJointNames() const; + AnimationHandlePointer createAnimationHandle(); const QList& getRunningAnimations() const { return _runningAnimations; } @@ -259,7 +260,6 @@ protected: bool _showTrueJointTransforms; - bool getJointStateAtIndex(int jointIndex, JointState& jointState) const; QVector _jointStates; class MeshState { @@ -281,13 +281,10 @@ protected: void simulateInternal(float deltaTime); /// Updates the state of the joint at the specified index. - void updateJointStates(); virtual void updateJointState(int index); virtual void updateVisibleJointStates(); - - glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); - + /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame @@ -297,8 +294,8 @@ protected: /// \param alignment /// \return true if joint exists bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(), - bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, - const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); + bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, + const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); /// Restores the indexed joint to its default position. /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index f1ff0e9e4f..4e7e413770 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -113,6 +113,8 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, int index1 = triangles[i * 3 + 1] + indexStartOffset; int index2 = triangles[i * 3 + 2] + indexStartOffset; + // TODO: skip triangles with a normal that points more negative-y than positive-y + glm::vec3 p0 = result.vertices[index0]; glm::vec3 p1 = result.vertices[index1]; glm::vec3 p2 = result.vertices[index2]; From 8173dc2c9a12cc28911b63010b91d1d10b42883a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 22 Jul 2015 14:21:42 -0700 Subject: [PATCH 013/109] Remove text extensions reference that isn't needed and which has changed underneath us. --- tests/rig/src/RigTests.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rig/src/RigTests.h b/tests/rig/src/RigTests.h index ddb8de9c2d..9f8ba22eb8 100644 --- a/tests/rig/src/RigTests.h +++ b/tests/rig/src/RigTests.h @@ -15,7 +15,7 @@ #include #include -#include "../QTestExtensions.h" +//#include "../QTestExtensions.h" // The QTest terminology is not consistent with itself or with industry: From ee334ff8266e1fbcaac8a275d28cd40b4b61d748 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 22 Jul 2015 18:43:10 -0700 Subject: [PATCH 014/109] JointStates are owned by Rig objects. Model, FaceModel, SkeletonModel call into their Rig pointer to access JointStates. --- interface/src/avatar/Avatar.cpp | 3 +- interface/src/avatar/AvatarManager.cpp | 5 +- interface/src/avatar/FaceModel.cpp | 48 +- interface/src/avatar/FaceModel.h | 6 +- interface/src/avatar/Head.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 131 +++--- interface/src/avatar/SkeletonModel.h | 6 +- interface/src/ui/overlays/ModelOverlay.cpp | 4 +- libraries/animation/src/AnimationHandle.cpp | 22 +- libraries/animation/src/AvatarRig.cpp | 51 +++ libraries/animation/src/AvatarRig.h | 28 ++ libraries/animation/src/EntityRig.cpp | 30 ++ libraries/animation/src/EntityRig.h | 28 ++ libraries/animation/src/Rig.cpp | 315 ++++++++----- libraries/animation/src/Rig.h | 74 +-- .../src/EntityTreeRenderer.cpp | 5 +- .../src/RenderableZoneEntityItem.cpp | 2 +- libraries/render-utils/src/Model.cpp | 421 +++--------------- libraries/render-utils/src/Model.h | 14 +- 19 files changed, 562 insertions(+), 635 deletions(-) create mode 100644 libraries/animation/src/AvatarRig.cpp create mode 100644 libraries/animation/src/AvatarRig.h create mode 100644 libraries/animation/src/EntityRig.cpp create mode 100644 libraries/animation/src/EntityRig.h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1d14a0f464..26e218d05b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -45,6 +45,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" +#include "EntityRig.h" using namespace std; @@ -965,7 +966,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (_unusedAttachments.size() > 0) { model = _unusedAttachments.takeFirst(); } else { - model = new Model(this); + model = new Model(std::make_shared(), this); } model->init(); _attachmentModels.append(model); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 730c77e9c3..ead330a41f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -35,6 +35,7 @@ #include "Menu.h" #include "MyAvatar.h" #include "SceneScriptingInterface.h" +#include "AvatarRig.h" // 70 times per second - target is 60hz, but this helps account for any small deviations // in the update loop @@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) : { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::make_shared(std::make_shared()); + _myAvatar = std::make_shared(std::make_shared()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared(std::make_shared())); + return AvatarSharedPointer(std::make_shared(std::make_shared())); } // virtual diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 16b370d459..4a5eadccd3 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -16,9 +16,11 @@ #include "Head.h" #include "Menu.h" -FaceModel::FaceModel(Head* owningHead) : +FaceModel::FaceModel(Head* owningHead, RigPointer rig) : + Model(rig, nullptr), _owningHead(owningHead) { + assert(_rig); } void FaceModel::simulate(float deltaTime, bool fullUpdate) { @@ -48,54 +50,58 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } } -void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * - glm::translate(state.getDefaultTranslationInConstrainedFrame()) * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * joint.preTransform * glm::mat4_cast(joint.preRotation))); glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), _owningHead->getTorsoTwist(), _owningHead->getFinalLeanSideways())); pitchYawRoll -= lean; - state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) - * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) - * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) - * joint.rotation, DEFAULT_PRIORITY); + _rig->setJointRotationInConstrainedFrame(index, + glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) + * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) + * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) + * joint.rotation, DEFAULT_PRIORITY); } -void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) { +void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) { // likewise with the eye joints // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. - glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * - glm::translate(state.getDefaultTranslationInConstrainedFrame()) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() + _owningHead->getSaccade() - model->getTranslation(), 1.0f)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation, DEFAULT_PRIORITY); + _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), + -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation, DEFAULT_PRIORITY); } void FaceModel::updateJointState(int index) { - JointState& state = _jointStates[index]; + const JointState& state = _rig->getJointState(index); const FBXJoint& joint = state.getFBXJoint(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // guard against out-of-bounds access to _jointStates - if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { + const JointState& parentState = _rig->getJointState(joint.parentIndex); if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint, index); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(this, parentState, joint, state); + maybeUpdateEyeRotation(this, parentState, joint, index); } } - Model::updateJointState(index); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->updateFaceJointState(index, parentTransform); } bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 6c14beb587..ce78c51e70 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -22,12 +22,12 @@ class FaceModel : public Model { public: - FaceModel(Head* owningHead); + FaceModel(Head* owningHead, RigPointer rig); virtual void simulate(float deltaTime, bool fullUpdate = true); - virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state); + virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); virtual void updateJointState(int index); /// Retrieve the positions of up to two eye meshes. diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 43e68557ce..0d07f171c0 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -23,6 +23,7 @@ #include "Util.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" +#include "AvatarRig.h" using namespace std; @@ -55,11 +56,10 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), - _faceModel(this), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { - } void Head::init() { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c8e6f03d69..765dc9160e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -30,7 +30,7 @@ enum StandingFootState { }; SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : - Model(parent, rig), + Model(rig, parent), _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), _boundingShape(), @@ -51,11 +51,13 @@ SkeletonModel::~SkeletonModel() { } void SkeletonModel::initJointStates(QVector states) { - Model::initJointStates(states); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; - if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) { glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -75,8 +77,8 @@ void SkeletonModel::initJointStates(QVector states) { // the SkeletonModel override of updateJointState() will clear the translation part // of its root joint and we need that done before we try to build shapes hence we // recompute all joint transforms at this time. - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); + for (int i = 0; i < _rig->getJointStateCount(); i++) { + _rig->updateJointState(i, parentTransform); } clearShapes(); @@ -168,7 +170,7 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) || jointIndex == getRightHandJointIndex()) { // get all shapes that have this hand as an ancestor in the skeleton heirarchy const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { + for (int i = 0; i < _rig->getJointStateCount(); i++) { const FBXJoint& joint = geometry.joints[i]; int parentIndex = joint.parentIndex; Shape* shape = _shapes[i]; @@ -211,7 +213,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) { } void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } // NOTE: 'position' is in model-frame @@ -226,16 +228,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) if (forearmLength < EPSILON) { return; } - JointState& state = _jointStates[jointIndex]; - glm::quat handRotation = state.getRotation(); + glm::quat handRotation; + if (!_rig->getJointStateRotation(jointIndex, handRotation)) { + return; + } // align hand with forearm float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; - state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY); + _rig->applyJointRotationDelta(jointIndex, + rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), + true, PALM_PRIORITY); } void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -261,43 +267,40 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f); setJointPosition(parentJointIndex, palmPosition + forearm, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); - JointState& parentState = _jointStates[parentJointIndex]; - parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY); + _rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY); } else { inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } } void SkeletonModel::updateJointState(int index) { - if (index < 0 && index >= _jointStates.size()) { - return; // bail - } - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(joint.parentIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + + const JointState joint = _rig->getJointState(index); + if (joint.getParentIndex() >= 0 && joint.getParentIndex() < _rig->getJointStateCount()) { + const JointState parentState = _rig->getJointState(joint.getParentIndex()); if (index == geometry.leanJointIndex) { - maybeUpdateLeanRotation(parentState, state); - + maybeUpdateLeanRotation(parentState, index); + } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, state); - + maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index); + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(parentState, joint, state); + maybeUpdateEyeRotation(parentState, joint.getFBXJoint(), index); } } - Model::updateJointState(index); + _rig->updateJointState(index, parentTransform); if (index == _geometry->getFBXGeometry().rootJointIndex) { - state.clearTransformTranslation(); + _rig->clearJointTransformTranslation(index); } } -void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) { +void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, int index) { if (!_owningAvatar->isMyAvatar()) { return; } @@ -305,24 +308,24 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, Joint glm::vec3 xAxis(1.0f, 0.0f, 0.0f); glm::vec3 yAxis(0.0f, 1.0f, 0.0f); glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame()); - state.setRotationInConstrainedFrame( - glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) - * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) - * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) - * state.getFBXJoint().rotation, LEAN_PRIORITY); + glm::quat inverse = glm::inverse(parentState.getRotation() * _rig->getJointDefaultRotationInParentFrame(index)); + _rig->setJointRotationInConstrainedFrame(index, + glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) + * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) + * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) + * _rig->getJointState(index).getFBXJoint().rotation, LEAN_PRIORITY); } -void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state); +void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { + _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index); } -void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state); +void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index) { + _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, index); } void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -331,9 +334,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { batch._glLineWidth(3.0f); do { const FBXJoint& joint = geometry.joints.at(jointIndex); - const JointState& jointState = _jointStates.at(jointIndex); + const JointState& jointState = _rig->getJointState(jointIndex); glm::vec3 position = _rotation * jointState.getPosition() + _translation; - glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation(); + glm::quat parentRotation = (joint.parentIndex == -1) ? + _rotation : + _rotation * _rig->getJointState(joint.parentIndex).getRotation(); float fanScale = directionSize * 0.75f; Transform transform = Transform(); @@ -464,14 +469,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); - JointState& shoulderState = _jointStates[shoulderJointIndex]; - shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY); - - JointState& elbowState = _jointStates[elbowJointIndex]; - elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY); - - JointState& handState = _jointStates[jointIndex]; - handState.setRotationInBindFrame(rotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(elbowJointIndex, + rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * + shoulderRotation, PALM_PRIORITY); + _rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY); } bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { @@ -526,7 +528,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP glm::quat worldFrameRotation; bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation); if (success) { - neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; + neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation; } return success; } @@ -636,7 +638,7 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::buildShapes() { - if (_geometry == NULL || _jointStates.isEmpty()) { + if (_geometry == NULL || _rig->jointStatesEmpty()) { return; } @@ -647,9 +649,8 @@ void SkeletonModel::buildShapes() { } float uniformScale = extractUniformScale(_scale); - const int numStates = _jointStates.size(); - for (int i = 0; i < numStates; i++) { - JointState& state = _jointStates[i]; + for (int i = 0; i < _rig->getJointStateCount(); i++) { + const JointState& state = _rig->getJointState(i); const FBXJoint& joint = state.getFBXJoint(); float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; @@ -683,7 +684,7 @@ void SkeletonModel::buildShapes() { void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms - int numStates = _jointStates.size(); + int numStates = _rig->getJointStateCount(); assert(numStates == _shapes.size()); QVector transforms; transforms.fill(glm::mat4(), numStates); @@ -694,11 +695,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < numStates; i++) { // compute the default transform of this joint - JointState& state = _jointStates[i]; + const JointState& state = _rig->getJointState(i); const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { - transforms[i] = _jointStates[i].getTransform(); + transforms[i] = _rig->getJointTransform(i); } else { glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) @@ -748,7 +749,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); + glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); } @@ -853,7 +854,7 @@ void SkeletonModel::cauterizeHead() { if (isActive()) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const int neckJointIndex = geometry.neckJointIndex; - if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) { + if (neckJointIndex > 0 && neckJointIndex < _rig->getJointStateCount()) { // lazy init of headBones if (_headBones.size() == 0) { @@ -861,13 +862,13 @@ void SkeletonModel::cauterizeHead() { } // preserve the translation for the neck - glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; + // glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; + glm::vec4 trans = _rig->getJointTransform(neckJointIndex)[3]; glm::vec4 zero(0, 0, 0, 0); for (const int &i : _headBones) { - JointState& joint = _jointStates[i]; glm::mat4 newXform(zero, zero, zero, trans); - joint.setTransform(newXform); - joint.setVisibleTransform(newXform); + _rig->setJointTransform(i, newXform); + _rig->setJointVisibleTransform(i, newXform); } } } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 6d33b3da7b..c4cd43b4df 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -134,9 +134,9 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); - void maybeUpdateLeanRotation(const JointState& parentState, JointState& state); - void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); - void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state); + void maybeUpdateLeanRotation(const JointState& parentState, int index); + void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index); void cauterizeHead(); void initHeadBones(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ed15e57d43..78091cd1a6 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -14,7 +14,7 @@ #include "Application.h" ModelOverlay::ModelOverlay() - : _model(), + : _model(nullptr), _modelTextures(QVariantMap()), _updateModel(false) { @@ -24,7 +24,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(), + _model(nullptr), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index 7f45fb600e..d11dcacfc6 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -159,15 +159,15 @@ void AnimationHandle::applyFrame(float frameIndex) { const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); float frameFraction = glm::fract(frameIndex); - QVector jointStates = _rig->getJointStates(); + assert(_rig->getJointStateCount() >= _jointMappings.size()); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState& state = jointStates[mapping]; - state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), - ceilFrame.rotations.at(i), - frameFraction), - _priority); + _rig->setJointRotationInConstrainedFrame(mapping, + safeMix(floorFrame.rotations.at(i), + ceilFrame.rotations.at(i), + frameFraction), + _priority); } } } @@ -176,10 +176,9 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - QVector jointStates = _rig->getJointStates(); - JointState& state = jointStates[mapping]; + JointState state = _rig->getJointState(mapping); if (_priority == state._animationPriority) { - state._animationPriority = newPriority; + _rig->setJointAnimatinoPriority(mapping, newPriority); } } } @@ -189,9 +188,8 @@ void AnimationHandle::restoreJoints() { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - QVector jointStates = _rig->getJointStates(); - JointState& state = jointStates[mapping]; - state.restoreRotation(1.0f, state._animationPriority); + JointState state = _rig->getJointState(mapping); + _rig->restoreJointRotation(mapping, 1.0f, state._animationPriority); } } } diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp new file mode 100644 index 0000000000..cf05e61cdb --- /dev/null +++ b/libraries/animation/src/AvatarRig.cpp @@ -0,0 +1,51 @@ +// +// AvatarRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 "AvatarRig.h" + +/// Updates the state of the joint at the specified index. +void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { + if (index < 0 && index >= _jointStates.size()) { + return; // bail + } + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} + + +void AvatarRig::updateFaceJointState(int index, glm::mat4 parentTransform) { + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h new file mode 100644 index 0000000000..dbffd8aa45 --- /dev/null +++ b/libraries/animation/src/AvatarRig.h @@ -0,0 +1,28 @@ +// +// AvatarRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 +// + +#ifndef hifi_AvatarRig_h +#define hifi_AvatarRig_h + +#include + +#include "Rig.h" + +class AvatarRig : public Rig { + Q_OBJECT + + public: + ~AvatarRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateFaceJointState(int index, glm::mat4 parentTransform); +}; + +#endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp new file mode 100644 index 0000000000..5ed1799671 --- /dev/null +++ b/libraries/animation/src/EntityRig.cpp @@ -0,0 +1,30 @@ +// +// EntityRig.cpp +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 "EntityRig.h" + +/// Updates the state of the joint at the specified index. +void EntityRig::updateJointState(int index, glm::mat4 parentTransform) { + JointState& state = _jointStates[index]; + const FBXJoint& joint = state.getFBXJoint(); + + // compute model transforms + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + state.computeTransform(parentTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } +} diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h new file mode 100644 index 0000000000..aa6a5fbd2b --- /dev/null +++ b/libraries/animation/src/EntityRig.h @@ -0,0 +1,28 @@ +// +// EntityRig.h +// libraries/animation/src/ +// +// Created by SethAlves on 2015-7-22. +// 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 +// + +#ifndef hifi_EntityRig_h +#define hifi_EntityRig_h + +#include + +#include "Rig.h" + +class EntityRig : public Rig { + Q_OBJECT + + public: + ~EntityRig() {} + virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateFaceJointState(int index, glm::mat4 parentTransform) { } +}; + +#endif // hifi_EntityRig_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 5e322e420f..12bfd606da 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -13,11 +13,7 @@ #include "Rig.h" -bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { - return _runningAnimations.removeOne(animationHandle); -} - -static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { +void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { handles.insert(it, handle); @@ -27,17 +23,27 @@ static void insertSorted(QList& handles, const Animation handles.append(handle); } +AnimationHandlePointer Rig::createAnimationHandle() { + AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); + _animationHandles.insert(handle); + return handle; +} + +bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { + return _runningAnimations.removeOne(animationHandle); +} + void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { - insertSorted(_runningAnimations, animationHandle); + insertSorted(_runningAnimations, animationHandle); } bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } -float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states) { +float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { _jointStates = states; - initJointTransforms(scale, offset); + initJointTransforms(parentTransform); int numStates = _jointStates.size(); float radius = 0.0f; @@ -52,11 +58,11 @@ float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVectorgetFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset); // * geometry.offset; XXX state.initTransform(parentTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); @@ -75,19 +78,30 @@ void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) { } } -void Rig::resetJoints() { +void Rig::clearJointTransformTranslation(int jointIndex) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].clearTransformTranslation(); +} + +void Rig::reset(const QVector& fbxJoints) { if (_jointStates.isEmpty()) { return; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint(); - _jointStates[i].setRotationInConstrainedFrame(fbxJoint.rotation, 0.0f); + _jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f); } } -bool Rig::getJointState(int index, glm::quat& rotation) const { +JointState Rig::getJointState(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return JointState(); + } + return _jointStates[jointIndex]; +} + +bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } @@ -105,12 +119,6 @@ bool Rig::getVisibleJointState(int index, glm::quat& rotation) const { return !state.rotationIsDefault(rotation); } -void Rig::updateVisibleJointStates() { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } -} - void Rig::clearJointState(int index) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -122,6 +130,18 @@ void Rig::clearJointStates() { _jointStates.clear(); } +void Rig::clearJointAnimationPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = 0.0f; + } +} + +void Rig::setJointAnimatinoPriority(int index, float newPriority) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index]._animationPriority = newPriority; + } +} + void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -133,99 +153,103 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float } } - -void Rig::clearJointAnimationPriority(int index) { +void Rig::restoreJointRotation(int index, float fraction, float priority) { if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; + _jointStates[index].restoreRotation(fraction, priority); } } -AnimationHandlePointer Rig::createAnimationHandle() { - AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); - _animationHandles.insert(handle); - return handle; -} - -bool Rig::getJointStateAtIndex(int jointIndex, JointState& jointState) const { +bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - jointState = _jointStates[jointIndex]; + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getPosition(); return true; } -void Rig::updateJointStates(glm::mat4 parentTransform) { +bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in model-frame + position = extractTranslation(_jointStates[jointIndex].getTransform()); + return true; +} + +bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _jointStates[jointIndex].getRotation(); + return true; +} + +bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getRotation(); + return true; +} + + +bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = translation + rotation * _jointStates[jointIndex].getVisiblePosition(); + return true; +} + +bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + result = rotation * _jointStates[jointIndex].getVisibleRotation(); + return true; +} + +glm::mat4 Rig::getJointTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getTransform(); +} + +glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + return _jointStates[jointIndex].getVisibleTransform(); +} + +void Rig::simulateInternal(glm::mat4 parentTransform) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i, parentTransform); } -} - -void Rig::updateJointState(int index, glm::mat4 parentTransform) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - // glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset) * geometryOffset; - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - -void Rig::resetAllTransformsChanged() { for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].resetTransformChanged(); } } -glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInBindFrame(rotation, priority, constrain); - endRotation = state.getRotationInBindFrame(); - return endRotation; -} - -glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInConstrainedFrame(targetRotation, priority, constrain); - endRotation = state.getRotationInConstrainedFrame(); - return endRotation; -} - -void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return; - } - _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); -} - -bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, - float priority, glm::mat4 parentTransform) { +bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - - if (freeLineage.isEmpty()) { return false; } @@ -304,19 +328,14 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm: return true; } -void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, - const glm::quat& targetRotation, float priority, glm::mat4 parentTransform) { +void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform) { // NOTE: targetRotation is from bind- to model-frame if (endIndex == -1 || _jointStates.isEmpty()) { return; } - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[endIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - if (freeLineage.isEmpty()) { return; } @@ -404,11 +423,11 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, } // recompute transforms from the top down - glm::mat4 parentTransform = topParentTransform; + glm::mat4 currentParentTransform = topParentTransform; for (int j = numFree - 1; j >= 0; --j) { JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(parentTransform); - parentTransform = freeState.getTransform(); + freeState.computeTransform(currentParentTransform); + currentParentTransform = freeState.getTransform(); } // measure our success @@ -420,14 +439,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, endState.setRotationInBindFrame(targetRotation, priority, true); } -bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) { +bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; foreach (int index, freeLineage) { JointState& state = _jointStates[index]; @@ -436,22 +451,78 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) { return true; } -float Rig::getLimbLength(int jointIndex, glm::vec3 scale) const { +float Rig::getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const { if (jointIndex == -1 || _jointStates.isEmpty()) { return 0.0f; } - - // const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint(); - const QVector& freeLineage = fbxJoint.freeLineage; - float length = 0.0f; float lengthScale = (scale.x + scale.y + scale.z) / 3.0f; for (int i = freeLineage.size() - 2; i >= 0; i--) { - int something = freeLineage.at(i); - const FBXJoint& fbxJointI = _jointStates[something].getFBXJoint(); - length += fbxJointI.distanceToParent * lengthScale; + length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale; } return length; } + +glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInBindFrame(rotation, priority, constrain); + endRotation = state.getRotationInBindFrame(); + return endRotation; +} + +glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::vec3(); + } + return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); +} + +glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { + glm::quat endRotation; + if (jointIndex == -1 || _jointStates.isEmpty()) { + return endRotation; + } + JointState& state = _jointStates[jointIndex]; + state.setRotationInConstrainedFrame(targetRotation, priority, constrain); + endRotation = state.getRotationInConstrainedFrame(); + return endRotation; +} + +void Rig::updateVisibleJointStates() { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } +} + +void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setTransform(newTransform); +} + +void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return; + } + _jointStates[jointIndex].setVisibleTransform(newTransform); +} + +void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return; + } + _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority); +} + +glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { + if (jointIndex == -1 || _jointStates.isEmpty()) { + return glm::quat(); + } + return _jointStates[jointIndex].getDefaultRotationInParentFrame(); +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 89ea99f162..87b0c7504e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -11,10 +11,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* TBD: - - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? + - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics? - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly? - + - Distribute some doc from here to the right files if it turns out to be correct: - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity. */ @@ -37,49 +37,69 @@ typedef std::shared_ptr RigPointer; class Rig : public QObject, public std::enable_shared_from_this { public: + + virtual ~Rig() {} + RigPointer getRigPointer() { return shared_from_this(); } + AnimationHandlePointer createAnimationHandle(); bool removeRunningAnimation(AnimationHandlePointer animationHandle); void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); const QList& getRunningAnimations() const { return _runningAnimations; } - float initJointStates(glm::vec3 scale, glm::vec3 offset, QVector states); - void initJointTransforms(glm::vec3 scale, glm::vec3 offset); - void resetJoints(); + float initJointStates(QVector states, glm::mat4 parentTransform); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; - int jointStateCount() const { return _jointStates.size(); } - bool getJointStateAtIndex(int jointIndex, JointState& jointState) const; + int getJointStateCount() const { return _jointStates.size(); } - void updateJointStates(glm::mat4 parentTransform); - void updateJointState(int index, glm::mat4 parentTransform); - void resetAllTransformsChanged(); - - bool getJointState(int index, glm::quat& rotation) const; + void initJointTransforms(glm::mat4 parentTransform); + void clearJointTransformTranslation(int jointIndex); + void reset(const QVector& fbxJoints); + bool getJointStateRotation(int index, glm::quat& rotation) const; + void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); + JointState getJointState(int jointIndex) const; bool getVisibleJointState(int index, glm::quat& rotation) const; - void updateVisibleJointStates(); void clearJointState(int index); void clearJointStates(); - void setJointState(int index, bool valid, const glm::quat& rotation, float priority); void clearJointAnimationPriority(int index); + void setJointAnimatinoPriority(int index, float newPriority); + void setJointState(int index, bool valid, const glm::quat& rotation, float priority); + void restoreJointRotation(int index, float fraction, float priority); + bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + + bool getJointPosition(int jointIndex, glm::vec3& position) const; + bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, + glm::vec3 translation, glm::quat rotation) const; + bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const; + glm::mat4 getJointTransform(int jointIndex) const; + void setJointTransform(int jointIndex, glm::mat4 newTransform); + glm::mat4 getJointVisibleTransform(int jointIndex) const; + void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); + void simulateInternal(glm::mat4 parentTransform); + bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, + const QVector& freeLineage, glm::mat4 parentTransform); + bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); + float getLimbLength(int jointIndex, const QVector& freeLineage, + const glm::vec3 scale, const QVector& fbxJoints) const; + glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false); + glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain = false); - void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); + glm::quat getJointDefaultRotationInParentFrame(int jointIndex); + void updateVisibleJointStates(); - QVector getJointStates() { return _jointStates; } + virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; + virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0; - AnimationHandlePointer createAnimationHandle(); - - bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, - float priority, glm::mat4 parentTransform); - void inverseKinematics(int endIndex, glm::vec3 targetPosition, - const glm::quat& targetRotation, float priority, glm::mat4 parentTransform); - bool restoreJointPosition(int jointIndex, float fraction, float priority); - float getLimbLength(int jointIndex, glm::vec3 scale) const; - -protected: + protected: QVector _jointStates; QSet _animationHandles; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index adc6ae9de2..2fcdd7f664 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -40,6 +40,7 @@ #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" #include "AddressManager.h" +#include "EntityRig.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll return model; } - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -728,7 +729,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c } // create the model and correctly initialize it with the new url - model = new Model(); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 2e03266253..bb0a35f7b0 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch } Model* RenderableZoneEntityItem::getModel() { - Model* model = new Model(); + Model* model = new Model(nullptr); model->setIsWireframe(true); model->init(); return model; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5a6908fc4a..af60c91664 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; -Model::Model(QObject* parent, RigPointer rig) : +Model::Model(RigPointer rig, QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), @@ -87,7 +87,7 @@ Model::Model(QObject* parent, RigPointer rig) : if (_viewState) { moveToThread(_viewState->getMainThread()); } - + setSnapModelToRegistrationPoint(true, glm::vec3(0.5f)); } @@ -252,22 +252,9 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.initTransform(parentTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->initJointTransforms(parentTransform); } void Model::init() { @@ -396,14 +383,8 @@ void Model::init() { } void Model::reset() { - if (_jointStates.isEmpty()) { - return; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f); - } - + _rig->reset(geometry.joints); _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -436,28 +417,30 @@ bool Model::updateGeometry() { const FBXGeometry& newGeometry = geometry->getFBXGeometry(); QVector newJointStates = createJointStates(newGeometry); - if (! _jointStates.isEmpty()) { + + if (! _rig->jointStatesEmpty()) { // copy the existing joint states const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { + it != oldGeometry.jointIndices.constEnd(); it++) { int oldIndex = it.value() - 1; int newIndex = newGeometry.getJointIndex(it.key()); if (newIndex != -1) { - newJointStates[newIndex].copyState(_jointStates[oldIndex]); + newJointStates[newIndex].copyState(_rig->getJointState(oldIndex)); } } - } + } + deleteGeometry(); _dilatedTextures.clear(); setGeometry(geometry); - + _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid initJointStates(newJointStates); needToRebuild = true; - } else if (_jointStates.isEmpty()) { + } else if (_rig->jointStatesEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); @@ -493,22 +476,9 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates(QVector states) { - _jointStates = states; - initJointTransforms(); - - int numStates = _jointStates.size(); - float radius = 0.0f; - for (int i = 0; i < numStates; ++i) { - float distance = glm::length(_jointStates[i].getPosition()); - if (distance > radius) { - radius = distance; - } - _jointStates[i].buildConstraint(); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } - _boundingRadius = radius; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _boundingRadius = _rig->initJointStates(states, parentTransform); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -1070,51 +1040,24 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { return translatedPoint; } - bool Model::getJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getJointStateRotation(index, rotation); } bool Model::getVisibleJointState(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getVisibleRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); + return _rig->getVisibleJointState(index, rotation); } void Model::clearJointState(int index) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); - } + _rig->clearJointState(index); } void Model::clearJointAnimationPriority(int index) { - if (_rig) { - _rig->clearJointAnimationPriority(index); - } else { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index]._animationPriority = 0.0f; - } - } + _rig->clearJointAnimationPriority(index); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); - } - } + _rig->setJointState(index, valid, rotation, priority); } int Model::getParentJointIndex(int jointIndex) const { @@ -1189,62 +1132,31 @@ void Model::setCollisionModelURL(const QUrl& url) { } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getPosition(); - return true; + return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); - return true; + return _rig->getJointPosition(jointIndex, position); } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation); } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointRotation(jointIndex, rotation); } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getRotation(); - return true; + return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); } bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - // position is in world-frame - position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); - return true; + return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); - return true; + return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation); } QStringList Model::getJointNames() const { @@ -1438,12 +1350,14 @@ void Model::updateClusterMatrices() { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } } @@ -1457,16 +1371,12 @@ void Model::simulateInternal(float deltaTime) { handle->simulate(deltaTime); } - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i); - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->simulateInternal(parentTransform); _shapesAreDirty = !_shapes.isEmpty(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1474,12 +1384,14 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + state.clusterMatrices[j] = + modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; } } } @@ -1492,261 +1404,42 @@ void Model::simulateInternal(float deltaTime) { } void Model::updateJointState(int index) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - -void Model::updateVisibleJointStates() { - if (_showTrueJointTransforms) { - // no need to update visible transforms - return; - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->updateJointState(index, parentTransform); } bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - if (freeLineage.isEmpty()) { - return false; + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + if (_rig->setJointPosition(jointIndex, position, rotation, useRotation, + lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) { + _shapesAreDirty = !_shapes.isEmpty(); + return true; } - if (lastFreeIndex == -1) { - lastFreeIndex = freeLineage.last(); - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - const int ITERATION_COUNT = 1; - glm::vec3 worldAlignment = alignment; - for (int i = 0; i < ITERATION_COUNT; i++) { - // first, try to rotate the end effector as close as possible to the target rotation, if any - glm::quat endRotation; - if (useRotation) { - JointState& state = _jointStates[jointIndex]; - - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - } - - // then, we go from the joint upwards, rotating the end as close as possible to the target - glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); - for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) { - int index = freeLineage.at(j); - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - if (!(joint.isFree || allIntermediatesFree)) { - continue; - } - glm::vec3 jointPosition = extractTranslation(state.getTransform()); - glm::vec3 jointVector = endPosition - jointPosition; - glm::quat oldCombinedRotation = state.getRotation(); - glm::quat combinedDelta; - float combinedWeight; - if (useRotation) { - combinedDelta = safeMix(rotation * glm::inverse(endRotation), - rotationBetween(jointVector, position - jointPosition), 0.5f); - combinedWeight = 2.0f; - - } else { - combinedDelta = rotationBetween(jointVector, position - jointPosition); - combinedWeight = 1.0f; - } - if (alignment != glm::vec3() && j > 1) { - jointVector = endPosition - jointPosition; - glm::vec3 positionSum; - for (int k = j - 1; k > 0; k--) { - int index = freeLineage.at(k); - updateJointState(index); - positionSum += extractTranslation(_jointStates.at(index).getTransform()); - } - glm::vec3 projectedCenterOfMass = glm::cross(jointVector, - glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); - glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); - const float LENGTH_EPSILON = 0.001f; - if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { - combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), - 1.0f / (combinedWeight + 1.0f)); - } - } - state.applyRotationDelta(combinedDelta, true, priority); - glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); - endPosition = actualDelta * jointVector + jointPosition; - if (useRotation) { - endRotation = actualDelta * endRotation; - } - } - } - - // now update the joint states from the top - for (int j = freeLineage.size() - 1; j >= 0; j--) { - updateJointState(freeLineage.at(j)); - } - _shapesAreDirty = !_shapes.isEmpty(); - - return true; + return false; } void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { - // NOTE: targetRotation is from bind- to model-frame - - if (endIndex == -1 || _jointStates.isEmpty()) { - return; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - if (freeLineage.isEmpty()) { - return; - } - int numFree = freeLineage.size(); - - // store and remember topmost parent transform - glm::mat4 topParentTransform; - { - int index = freeLineage.last(); - const JointState& state = _jointStates.at(index); - const FBXJoint& joint = state.getFBXJoint(); - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - } else { - topParentTransform = _jointStates[parentIndex].getTransform(); - } - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - - // keep track of the position of the end-effector - JointState& endState = _jointStates[endIndex]; - glm::vec3 endPosition = endState.getPosition(); - float distanceToGo = glm::distance(targetPosition, endPosition); - - const int MAX_ITERATION_COUNT = 2; - const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm - int numIterations = 0; - do { - ++numIterations; - // moving up, rotate each free joint to get endPosition closer to target - for (int j = 1; j < numFree; j++) { - int nextIndex = freeLineage.at(j); - JointState& nextState = _jointStates[nextIndex]; - FBXJoint nextJoint = nextState.getFBXJoint(); - if (! nextJoint.isFree) { - continue; - } - - glm::vec3 pivot = nextState.getPosition(); - glm::vec3 leverArm = endPosition - pivot; - float leverLength = glm::length(leverArm); - if (leverLength < EPSILON) { - continue; - } - glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); - - // We want to mix the shortest rotation with one that will pull the system down with gravity - // so that limbs don't float unrealistically. To do this we compute a simplified center of mass - // where each joint has unit mass and we don't bother averaging it because we only need direction. - if (j > 1) { - - glm::vec3 centerOfMass(0.0f); - for (int k = 0; k < j; ++k) { - int massIndex = freeLineage.at(k); - centerOfMass += _jointStates[massIndex].getPosition() - pivot; - } - // the gravitational effect is a rotation that tends to align the two cross products - const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); - glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), - glm::cross(worldAlignment, leverArm)); - - float gravityAngle = glm::angle(gravityDelta); - const float MIN_GRAVITY_ANGLE = 0.1f; - float mixFactor = 0.5f; - if (gravityAngle < MIN_GRAVITY_ANGLE) { - // the final rotation is a mix of the two - mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; - } - deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); - } - - // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose - // in the process. This provides stability to the IK solution for most models. - glm::quat oldNextRotation = nextState.getRotation(); - float mixFactor = 0.03f; - nextState.mixRotationDelta(deltaRotation, mixFactor, priority); - - // measure the result of the rotation which may have been modified by - // blending and constraints - glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); - endPosition = pivot + actualDelta * leverArm; - } - - // recompute transforms from the top down - glm::mat4 parentTransform = topParentTransform; - for (int j = numFree - 1; j >= 0; --j) { - JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(parentTransform); - parentTransform = freeState.getTransform(); - } - - // measure our success - endPosition = endState.getPosition(); - distanceToGo = glm::distance(targetPosition, endPosition); - } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); - - // set final rotation of the end joint - endState.setRotationInBindFrame(targetRotation, priority, true); - + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); _shapesAreDirty = !_shapes.isEmpty(); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - - foreach (int index, freeLineage) { - JointState& state = _jointStates[index]; - state.restoreRotation(fraction, priority); - } - return true; + return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return 0.0f; - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - float length = 0.0f; - float lengthScale = (_scale.x + _scale.y + _scale.z) / 3.0f; - for (int i = freeLineage.size() - 2; i >= 0; i--) { - length += geometry.joints.at(freeLineage.at(i)).distanceToParent * lengthScale; - } - return length; + return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } void Model::renderJointCollisionShapes(float alpha) { @@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); - _jointStates.clear(); + _rig->clearJointStates(); _meshStates.clear(); clearShapes(); @@ -1945,7 +1638,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran args, locations); { - updateVisibleJointStates(); + if (!_showTrueJointTransforms) { + _rig->updateVisibleJointStates(); + } // else no need to update visible transforms } // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1b4df6821c..f8d8cbbd74 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,7 +64,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(QObject* parent = nullptr, RigPointer rig = nullptr); + Model(RigPointer rig, QObject* parent = nullptr); virtual ~Model(); /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension @@ -162,7 +162,7 @@ public: const QSharedPointer getCollisionGeometry(bool delayLoad = true); /// Returns the number of joint states in the model. - int getJointStateCount() const { return _jointStates.size(); } + int getJointStateCount() const { return _rig->getJointStateCount(); } /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) @@ -224,9 +224,9 @@ public: void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } - QVector& getJointStates() { return _jointStates; } - const QVector& getJointStates() const { return _jointStates; } - + // QVector& getJointStates() { return _rig->getJointStates(); } + // const QVector& getJointStates() const { return _jointStates; } + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) @@ -259,8 +259,6 @@ protected: glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to bool _showTrueJointTransforms; - - QVector _jointStates; class MeshState { public: @@ -283,8 +281,6 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); - virtual void updateVisibleJointStates(); - /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame From 5a0c1a94020a23d953916cc480b52d454e1c8bc2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 22 Jul 2015 21:03:48 -0700 Subject: [PATCH 015/109] Checkpoint testing stuff. --- libraries/animation/src/Rig.h | 4 +++- libraries/avatars/src/AvatarData.cpp | 1 + libraries/avatars/src/AvatarData.h | 3 +++ tests/rig/CMakeLists.txt | 2 +- tests/rig/src/RigTests.cpp | 15 +++++++++++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 89ea99f162..3819917be0 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -11,12 +11,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* TBD: - - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)? + - What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? +Is there common/copied code (e.g., ScriptableAvatar::update)? - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics? - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly? - Distribute some doc from here to the right files if it turns out to be correct: - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity. + But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject is to AnimationPointer? */ #ifndef __hifi__Rig__ diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3a3b895c66..942dbeeaf7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1087,6 +1087,7 @@ void AvatarData::setJointMappingsFromNetworkReply() { } networkReply->deleteLater(); + emit jointsLoaded(); } void AvatarData::sendAvatarDataPacket() { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a020be0f7a..285460651a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -312,6 +312,9 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } +signals: + void jointsLoaded(); // So that test cases or anyone waiting on asynchronous loading can be informed. + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); diff --git a/tests/rig/CMakeLists.txt b/tests/rig/CMakeLists.txt index 2e9dbc9424..5e965c3ee8 100644 --- a/tests/rig/CMakeLists.txt +++ b/tests/rig/CMakeLists.txt @@ -1,7 +1,7 @@ # Declare dependencies macro (setup_testcase_dependencies) # link in the shared libraries - link_hifi_libraries(shared animation gpu fbx model) + link_hifi_libraries(shared animation gpu fbx model avatars networking audio) copy_dlls_beside_windows_executable() endmacro () diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index ca07cda844..37f7a2bbab 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -40,11 +40,26 @@ */ #include +//#include "FSTReader.h" +// There are two good ways we could organize this: +// 1. Create a MyAvatar the same way that Interface does, and poke at it. +// We can't do that because MyAvatar (and even Avatar) are in interface, not a library, and our build system won't allow that dependency. +// 2. Create just the minimum skeleton in the most direct way possible, using only very basic library APIs (such as fbx). +// I don't think we can do that because not everything we need is exposed directly from, e.g., the fst and fbx readers. +// So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and +// duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path. +#include "AvatarData.h" #include "RigTests.h" QTEST_MAIN(RigTests) void RigTests::initTestCase() { + AvatarData avatar; + QEventLoop loop; // Create an event loop that will quit when we get the finished signal + QObject::connect(&avatar, &AvatarData::jointsLoaded, &loop, &QEventLoop::quit); + avatar.setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach + //std::cout << "sleep start" << std::endl; + loop.exec(); // Nothing is going to happen on this whole run thread until we get this _rig = new Rig(); } From 6657026a120d9bfe51602a3be8eb3140572c3538 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 23 Jul 2015 08:34:44 -0700 Subject: [PATCH 016/109] remove cruft: legacy shapes for skeleton bones --- interface/src/avatar/Hand.cpp | 59 -------- interface/src/avatar/Hand.h | 4 - interface/src/avatar/SkeletonModel.cpp | 140 +------------------ interface/src/avatar/SkeletonModel.h | 6 - libraries/fbx/src/FBXReader.cpp | 46 ++---- libraries/fbx/src/FBXReader.h | 4 - libraries/fbx/src/OBJReader.cpp | 5 - libraries/render-utils/src/GeometryCache.cpp | 2 +- 8 files changed, 13 insertions(+), 253 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 63604d710d..017f11a610 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -40,65 +40,6 @@ void Hand::simulate(float deltaTime, bool isMine) { } } -// We create a static CollisionList that is recycled for each collision test. -const float MAX_COLLISIONS_PER_AVATAR = 32; -static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR); - -void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { - if (!avatar || avatar == _owningAvatar) { - // don't collide hands against ourself (that is done elsewhere) - return; - } - - const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel(); - int jointIndices[2]; - jointIndices[0] = skeletonModel.getLeftHandJointIndex(); - jointIndices[1] = skeletonModel.getRightHandJointIndex(); - - for (size_t i = 0; i < 2; i++) { - int jointIndex = jointIndices[i]; - if (jointIndex < 0) { - continue; - } - - handCollisions.clear(); - QVector shapes; - skeletonModel.getHandShapes(jointIndex, shapes); - - if (avatar->findCollisions(shapes, handCollisions)) { - glm::vec3 totalPenetration(0.0f); - glm::vec3 averageContactPoint; - for (int j = 0; j < handCollisions.size(); ++j) { - CollisionInfo* collision = handCollisions.getCollision(j); - totalPenetration += collision->_penetration; - averageContactPoint += collision->_contactPoint; - } - if (isMyHand) { - // our hand against other avatar - // TODO: resolve this penetration when we don't think the other avatar will yield - //palm.addToPenetration(averagePenetration); - } else { - // someone else's hand against MyAvatar - // TODO: submit collision info to MyAvatar which should lean accordingly - averageContactPoint /= (float)handCollisions.size(); - avatar->applyCollision(averageContactPoint, totalPenetration); - - CollisionInfo collision; - collision._penetration = totalPenetration; - collision._contactPoint = averageContactPoint; - emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision); - } - } - } -} - -void Hand::resolvePenetrations() { - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - palm.resolvePenetrations(); - } -} - void Hand::render(RenderArgs* renderArgs, bool isMine) { gpu::Batch& batch = *renderArgs->_batch; if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index ddc9d13489..750633959a 100644 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -40,10 +40,6 @@ public: void simulate(float deltaTime, bool isMine); void render(RenderArgs* renderArgs, bool isMine); - void collideAgainstAvatar(Avatar* avatar, bool isMyHand); - - void resolvePenetrations(); - private: // disallow copies of the Hand, copy of owning Avatar is disallowed too Hand(const Hand&); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index d92481494a..318db274a9 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -159,41 +159,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { _boundingShape.setRotation(_rotation); } -void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { - if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { - return; - } - if (jointIndex == getLeftHandJointIndex() - || jointIndex == getRightHandJointIndex()) { - // get all shapes that have this hand as an ancestor in the skeleton heirarchy - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - int parentIndex = joint.parentIndex; - Shape* shape = _shapes[i]; - if (i == jointIndex) { - // this shape is the hand - if (shape) { - shapes.push_back(shape); - } - if (parentIndex != -1 && _shapes[parentIndex]) { - // also add the forearm - shapes.push_back(_shapes[parentIndex]); - } - } else if (shape) { - while (parentIndex != -1) { - if (parentIndex == jointIndex) { - // this shape is a child of the hand - shapes.push_back(shape); - break; - } - parentIndex = geometry.joints[parentIndex].parentIndex; - } - } - } - } -} - void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { renderJointConstraints(batch, getRightHandJointIndex()); renderJointConstraints(batch, getLeftHandJointIndex()); @@ -644,46 +609,12 @@ void SkeletonModel::buildShapes() { // rootJointIndex == -1 if the avatar model has no skeleton return; } - - float uniformScale = extractUniformScale(_scale); - const int numStates = _jointStates.size(); - for (int i = 0; i < numStates; i++) { - JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - float radius = uniformScale * joint.boneRadius; - float halfHeight = 0.5f * uniformScale * joint.distanceToParent; - Shape::Type type = joint.shapeType; - int parentIndex = joint.parentIndex; - if (parentIndex == -1 || radius < EPSILON) { - type = INVALID_SHAPE; - } else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) { - // this shape is forced to be a sphere - type = SPHERE_SHAPE; - } - Shape* shape = NULL; - if (type == SPHERE_SHAPE) { - shape = new SphereShape(radius); - shape->setEntity(this); - } else if (type == CAPSULE_SHAPE) { - assert(parentIndex != -1); - shape = new CapsuleShape(radius, halfHeight); - shape->setEntity(this); - } - if (shape && parentIndex != -1) { - // always disable collisions between joint and its parent - disableCollisions(i, parentIndex); - } - _shapes.push_back(shape); - } - - // This method moves the shapes to their default positions in Model frame. computeBoundingShape(geometry); } void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms int numStates = _jointStates.size(); - assert(numStates == _shapes.size()); QVector transforms; transforms.fill(glm::mat4(), numStates); @@ -704,39 +635,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; } - // Each joint contributes its point to the bounding box + // Each joint contributes a sphere at its position + glm::vec3 axis(joint.boneRadius); glm::vec3 jointPosition = extractTranslation(transforms[i]); - totalExtents.addPoint(jointPosition); - - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - - // Each joint with a shape contributes to the totalExtents: a box - // that contains the sphere centered at the end of the joint with radius of the bone. - - // TODO: skip hand and arm shapes for bounding box calculation - int type = shape->getType(); - if (type == CAPSULE_SHAPE) { - // add the two furthest surface points of the capsule - CapsuleShape* capsule = static_cast(shape); - float radius = capsule->getRadius(); - glm::vec3 axis(radius); - Extents shapeExtents; - shapeExtents.reset(); - shapeExtents.addPoint(jointPosition + axis); - shapeExtents.addPoint(jointPosition - axis); - totalExtents.addExtents(shapeExtents); - } else if (type == SPHERE_SHAPE) { - float radius = shape->getBoundingRadius(); - glm::vec3 axis(radius); - Extents shapeExtents; - shapeExtents.reset(); - shapeExtents.addPoint(jointPosition + axis); - shapeExtents.addPoint(jointPosition - axis); - totalExtents.addExtents(shapeExtents); - } + totalExtents.addPoint(jointPosition + axis); + totalExtents.addPoint(jointPosition - axis); } // compute bounding shape parameters @@ -752,43 +655,8 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingRadius = 0.5f * glm::length(diagonal); } -void SkeletonModel::resetShapePositionsToDefaultPose() { - // DEBUG method. - // Moves shapes to the joint default locations for debug visibility into - // how the bounding shape is computed. - - if (!_geometry || _shapes.isEmpty()) { - // geometry or joints have not yet been created - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty()) { - return; - } - - // The shapes are moved to their default positions in computeBoundingShape(). - computeBoundingShape(geometry); - - // Then we move them into world frame for rendering at the Model's location. - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setTranslation(_translation + _rotation * shape->getTranslation()); - shape->setRotation(_rotation * shape->getRotation()); - } - } - _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); -} - void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { const int BALL_SUBDIVISIONS = 10; - if (_shapes.isEmpty()) { - // the bounding shape has not been propery computed - // so no need to render it - return; - } // draw a blue sphere at the capsule endpoint glm::vec3 endPoint; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 3d63238cf2..9d0c51e521 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -32,10 +32,6 @@ public: void simulate(float deltaTime, bool fullUpdate = true); - /// \param jointIndex index of hand joint - /// \param shapes[out] list in which is stored pointers to hand shapes - void getHandShapes(int jointIndex, QVector& shapes) const; - void renderIKConstraints(gpu::Batch& batch); /// Returns the index of the left hand joint, or -1 if not found. @@ -106,8 +102,6 @@ public: const CapsuleShape& getBoundingShape() const { return _boundingShape; } const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; } - void resetShapePositionsToDefaultPose(); // DEBUG method - bool hasSkeleton(); float getHeadClipDistance() const { return _headClipDistance; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 4d7bff4df0..f67fe133f2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1198,7 +1198,7 @@ class JointShapeInfo { public: JointShapeInfo() : numVertices(0), sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0), - averageVertex(0.0f), boneBegin(0.0f), averageRadius(0.0f) { + boneBegin(0.0f), averageRadius(0.0f) { } // NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin @@ -1206,9 +1206,8 @@ public: float sumVertexWeights; // sum of all vertex weights float sumWeightedRadii; // sum of weighted vertices int numVertexWeights; // num vertices that contributed to sums - glm::vec3 averageVertex;// average of all mesh vertices (in joint frame) glm::vec3 boneBegin; // parent joint location (in joint frame) - float averageRadius; // average distance from mesh points to averageVertex + float averageRadius; }; class AnimationCurve { @@ -2219,8 +2218,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, joint.boneRadius = 0.0f; joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; - joint.shapePosition = glm::vec3(0.0f); - joint.shapeType = INVALID_SHAPE; foreach (const QString& childID, childMap.values(modelID)) { QString type = typeFlags.value(childID); @@ -2490,7 +2487,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, int jointIndex = fbxCluster.jointIndex; FBXJoint& joint = geometry.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; - glm::quat rotateMeshToJoint = glm::inverse(extractRotation(transformJointToMesh)); glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; @@ -2524,8 +2520,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; - glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.averageVertex += vertexInJointFrame; ++jointShapeInfo.numVertices; } @@ -2571,7 +2565,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; - glm::quat rotateMeshToJoint = glm::inverse(extractRotation(transformJointToMesh)); glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; @@ -2594,9 +2587,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, jointShapeInfo.sumVertexWeights += radiusWeight; jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; - - glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.averageVertex += vertexInJointFrame; averageVertex += vertex; } int numVertices = extracted.mesh.vertices.size(); @@ -2622,7 +2612,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } - // now that all joints have been scanned, compute a collision shape for each joint + // now that all joints have been scanned, compute a radius for each bone glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; @@ -2640,40 +2630,20 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; } - // we use a capsule if the joint had ANY mesh vertices successfully projected onto the bone + // the joint is "capsule-like" if it had ANY mesh vertices successfully projected onto the bone // AND its boneRadius is not too close to zero bool collideLikeCapsule = jointShapeInfo.numVertexWeights > 0 && glm::length(jointShapeInfo.boneBegin) > EPSILON; - if (collideLikeCapsule) { - joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin); - joint.shapePosition = 0.5f * jointShapeInfo.boneBegin; - joint.shapeType = CAPSULE_SHAPE; - } else { - // collide the joint like a sphere - joint.shapeType = SPHERE_SHAPE; - if (jointShapeInfo.numVertices > 0) { - jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; - joint.shapePosition = jointShapeInfo.averageVertex; - } else { - joint.shapePosition = glm::vec3(0.0f); - } + if (!collideLikeCapsule) { + // this joint's mesh did not successfully project onto the bone axis + // so it isn't "capsule-like" and we need to estimate its radius a different way: + // the average radius to the average point. if (jointShapeInfo.numVertexWeights == 0 && jointShapeInfo.numVertices > 0) { - // the bone projection algorithm was not able to compute the joint radius - // so we use an alternative measure jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices; joint.boneRadius = jointShapeInfo.averageRadius; } - - float distanceFromEnd = glm::length(joint.shapePosition); - float distanceFromBegin = glm::distance(joint.shapePosition, jointShapeInfo.boneBegin); - if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) { - // The shape is further from both joint endpoints than the endpoints are from each other - // which probably means the model has a bad transform somewhere. We disable this shape - // by setting its type to INVALID_SHAPE. - joint.shapeType = INVALID_SHAPE; - } } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 200cd4a121..fbb303a93b 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -78,9 +77,6 @@ public: glm::quat inverseBindRotation; glm::mat4 bindTransform; QString name; - glm::vec3 shapePosition; // in joint frame - glm::quat shapeRotation; // in joint frame - quint8 shapeType; bool isSkeletonJoint; }; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 2ec80e3d85..35ba437745 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -427,8 +427,6 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); geometry.joints[0].name = "OBJ"; - geometry.joints[0].shapePosition = glm::vec3(0, 0, 0); - geometry.joints[0].shapeType = SPHERE_SHAPE; geometry.joints[0].isSkeletonJoint = true; geometry.jointIndices["x"] = 1; @@ -604,9 +602,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " inverseBindRotation" << joint.inverseBindRotation; qCDebug(modelformat) << " bindTransform" << joint.bindTransform; qCDebug(modelformat) << " name" << joint.name; - qCDebug(modelformat) << " shapePosition" << joint.shapePosition; - qCDebug(modelformat) << " shapeRotation" << joint.shapeRotation; - qCDebug(modelformat) << " shapeType" << joint.shapeType; qCDebug(modelformat) << " isSkeletonJoint" << joint.isSkeletonJoint; } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 6c03d57de3..cd63f7303a 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1780,7 +1780,7 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), - glm::mat4(), QString(""), glm::vec3(), glm::quat(), SHAPE_TYPE_NONE, false}; + glm::mat4(), QString(""), false}; _geometry.joints.append(joint); _geometry.leftEyeJointIndex = -1; _geometry.rightEyeJointIndex = -1; From 4566d16402236ac77a7b7784e5387159d6837ac8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 23 Jul 2015 13:03:38 -0700 Subject: [PATCH 017/109] move Model's AnimationHandles to Rig. Move a bunch of Model methods from public to protected --- interface/src/avatar/Avatar.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 4 +- libraries/animation/src/Rig.cpp | 15 +- libraries/animation/src/Rig.h | 3 +- libraries/render-utils/src/Model.cpp | 25 +-- libraries/render-utils/src/Model.h | 279 +++++++++++++-------------- 6 files changed, 161 insertions(+), 169 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 10866fa9ca..2bc7e788ad 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -823,7 +823,7 @@ QVector Avatar::getJointRotations() const { } QVector jointRotations(_skeletonModel.getJointStateCount()); for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) { - _skeletonModel.getJointState(i, jointRotations[i]); + _skeletonModel.getJointRotation(i, jointRotations[i]); } return jointRotations; } @@ -833,7 +833,7 @@ glm::quat Avatar::getJointRotation(int index) const { return AvatarData::getJointRotation(index); } glm::quat rotation; - _skeletonModel.getJointState(index, rotation); + _skeletonModel.getJointRotation(index, rotation); return rotation; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 56f5241316..178ebc0487 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -216,10 +216,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model - _jointData.resize(_skeletonModel.getJointStateCount()); + _jointData.resize(_rig->getJointStateCount()); for (int i = 0; i < _jointData.size(); i++) { JointData& data = _jointData[i]; - data.valid = _skeletonModel.getJointState(i, data.rotation); + data.valid = _rig->getJointStateRotation(i, data.rotation); } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 12bfd606da..0a889fce88 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -41,6 +41,14 @@ bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.contains(animationHandle); } +void Rig::deleteAnimations() { + for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { + (*it)->clearJoints(); + it = _animationHandles.erase(it); + } +} + + float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { _jointStates = states; initJointTransforms(parentTransform); @@ -235,7 +243,12 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { return _jointStates[jointIndex].getVisibleTransform(); } -void Rig::simulateInternal(glm::mat4 parentTransform) { +void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform) { + // update animations + foreach (const AnimationHandlePointer& handle, _runningAnimations) { + handle->simulate(deltaTime); + } + for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i, parentTransform); } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d282bf8f4..e60116a35e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -49,6 +49,7 @@ public: void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); const QList& getRunningAnimations() const { return _runningAnimations; } + void deleteAnimations(); float initJointStates(QVector states, glm::mat4 parentTransform); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; @@ -81,7 +82,7 @@ public: void setJointTransform(int jointIndex, glm::mat4 newTransform); glm::mat4 getJointVisibleTransform(int jointIndex) const; void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); - void simulateInternal(glm::mat4 parentTransform); + void simulateInternal(float deltaTime, glm::mat4 parentTransform); bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, const QVector& freeLineage, glm::mat4 parentTransform); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index af60c91664..bd33e75207 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1365,18 +1365,13 @@ void Model::updateClusterMatrices() { void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints - - // update animations - foreach (const AnimationHandlePointer& handle, _runningAnimations) { - handle->simulate(deltaTime); - } const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->simulateInternal(parentTransform); + _rig->simulateInternal(deltaTime, parentTransform); _shapesAreDirty = !_shapes.isEmpty(); - + glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1512,21 +1507,13 @@ void Model::deleteGeometry() { _rig->clearJointStates(); _meshStates.clear(); clearShapes(); - - for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { - AnimationHandlePointer handle = it->lock(); - if (handle) { - handle->clearJoints(); - it++; - } else { - it = _animationHandles.erase(it); - } - } - + + _rig->deleteAnimations(); + if (_geometry) { _geometry->clearLoadPriority(this); } - + _blendedBlendshapeCoefficients.clear(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f8d8cbbd74..30c5211990 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -57,7 +57,7 @@ inline uint qHash(const std::shared_ptr& a, uint seed) { /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { Q_OBJECT - + public: typedef RenderArgs::RenderMode RenderMode; @@ -66,58 +66,7 @@ public: Model(RigPointer rig, QObject* parent = nullptr); virtual ~Model(); - - /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); - bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled - bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit - const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to - void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions); - void setSnapModelToCenter(bool snapModelToCenter) { - setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f)); - }; - bool getSnapModelToCenter() { - return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f); - } - - void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); - bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } - - void setScale(const glm::vec3& scale); - const glm::vec3& getScale() const { return _scale; } - - void setOffset(const glm::vec3& offset); - const glm::vec3& getOffset() const { return _offset; } - - void setPupilDilation(float dilation) { _pupilDilation = dilation; } - float getPupilDilation() const { return _pupilDilation; } - - void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } - const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - - bool isActive() const { return _geometry && _geometry->isLoaded(); } - - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } - - void setVisibleInScene(bool newValue, std::shared_ptr scene); - bool isVisible() const { return _isVisible; } - - bool isLoaded() const { return _geometry && _geometry->isLoaded(); } - bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } - - void init(); - void reset(); - virtual void simulate(float deltaTime, bool fullUpdate = true); - - void renderSetup(RenderArgs* args); - - // new Scene/Engine rendering support - bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } - bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoaded(); } - bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); - bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters); - void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -127,22 +76,127 @@ public: bool retainCurrent = false, bool delayLoad = false); const QUrl& getURL() const { return _url; } + // new Scene/Engine rendering support + void setVisibleInScene(bool newValue, std::shared_ptr scene); + bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } + bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { + return !_needsReload && isRenderable() && isActive() && isLoaded(); + } + bool initWhenReady(render::ScenePointer scene); + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + bool addToScene(std::shared_ptr scene, + render::PendingChanges& pendingChanges, + render::Item::Status::Getters& statusGetters); + void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + void renderSetup(RenderArgs* args); + bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } + virtual void renderJointCollisionShapes(float alpha); + + bool isVisible() const { return _isVisible; } + + AABox getPartBounds(int meshIndex, int partIndex); + void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); + + bool maybeStartBlender(); + + /// Sets blended vertices computed in a separate thread. + void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, + const QVector& vertices, const QVector& normals); + + bool isLoaded() const { return _geometry && _geometry->isLoaded(); } + bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } + + void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } + bool isWireframe() const { return _isWireframe; } + + void init(); + void reset(); + + void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions); + + void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); + bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } + + virtual void simulate(float deltaTime, bool fullUpdate = true); + + /// Returns a reference to the shared geometry. + const QSharedPointer& getGeometry() const { return _geometry; } + + bool isActive() const { return _geometry && _geometry->isLoaded(); } + + Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) + { _geometry->setTextureWithNameToURL(name, url); } + + bool convexHullContains(glm::vec3 point); + + QStringList getJointNames() const; + + /// Sets the joint state at the specified index. + void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); + + bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); + // Set the model to use for collisions Q_INVOKABLE void setCollisionModelURL(const QUrl& url); const QUrl& getCollisionURL() const { return _collisionUrl; } - - void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } - bool isWireframe() const { return _isWireframe; } - + + /// Returns a reference to the shared collision geometry. + const QSharedPointer getCollisionGeometry(bool delayLoad = true); + + void setOffset(const glm::vec3& offset); + const glm::vec3& getOffset() const { return _offset; } + /// Sets the distance parameter used for LOD computations. void setLODDistance(float distance) { _lodDistance = distance; } - + + const QList& getRunningAnimations() const { return _rig->getRunningAnimations(); } + /// Clear the joint animation priority + void clearJointAnimationPriority(int index); + + void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); + bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled + + void setSnapModelToCenter(bool snapModelToCenter) { + setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f)); + }; + bool getSnapModelToCenter() { + return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f); + } + + /// Returns the number of joint states in the model. + int getJointStateCount() const { return _rig->getJointStateCount(); } + bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; + bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; + bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; + /// \param jointIndex index of joint in model structure + /// \param rotation[out] rotation of joint in model-frame + /// \return true if joint exists + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); + /// Returns the extents of the model in its bind pose. Extents getBindExtents() const; /// Returns the extents of the model's mesh Extents getMeshExtents() const; + void setScale(const glm::vec3& scale); + const glm::vec3& getScale() const { return _scale; } + + /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension + bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit + const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to + +protected: + + void setPupilDilation(float dilation) { _pupilDilation = dilation; } + float getPupilDilation() const { return _pupilDilation; } + + void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } + const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; @@ -155,15 +209,6 @@ public: /// Returns the scaled equivalent of a point in model space. glm::vec3 calculateScaledOffsetPoint(const glm::vec3& point) const; - /// Returns a reference to the shared geometry. - const QSharedPointer& getGeometry() const { return _geometry; } - - /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry(bool delayLoad = true); - - /// Returns the number of joint states in the model. - int getJointStateCount() const { return _rig->getJointStateCount(); } - /// Fetches the joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getJointState(int index, glm::quat& rotation) const; @@ -171,25 +216,15 @@ public: /// Fetches the visible joint state at the specified index. /// \return whether or not the joint state is "valid" (that is, non-default) bool getVisibleJointState(int index, glm::quat& rotation) const; - + /// Clear the joint states void clearJointState(int index); - - /// Clear the joint animation priority - void clearJointAnimationPriority(int index); - - /// Sets the joint state at the specified index. - void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); - + /// Returns the index of the parent of the indexed joint, or -1 if not found. int getParentJointIndex(int jointIndex) const; - + /// Returns the index of the last free ancestor of the indexed joint, or -1 if not found. int getLastFreeJointIndex(int jointIndex) const; - - bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; - bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; - bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; @@ -199,52 +234,15 @@ public: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; - /// \param jointIndex index of joint in model structure - /// \param rotation[out] rotation of joint in model-frame - /// \return true if joint exists - bool getJointRotation(int jointIndex, glm::quat& rotation) const; - - QStringList getJointNames() const; - - AnimationHandlePointer createAnimationHandle(); - - const QList& getRunningAnimations() const { return _runningAnimations; } - // virtual overrides from PhysicsEntity virtual void buildShapes(); virtual void updateShapePositions(); - virtual void renderJointCollisionShapes(float alpha); - - bool maybeStartBlender(); - - /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, - const QVector& vertices, const QVector& normals); - void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } - // QVector& getJointStates() { return _rig->getJointStates(); } - // const QVector& getJointStates() const { return _jointStates; } - - void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); - - Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) - { _geometry->setTextureWithNameToURL(name, url); } - - bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); - bool convexHullContains(glm::vec3 point); - - AABox getPartBounds(int meshIndex, int partIndex); - void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent); - - bool initWhenReady(render::ScenePointer scene); - -protected: QSharedPointer _geometry; void setGeometry(const QSharedPointer& newGeometry); - + glm::vec3 _scale; glm::vec3 _offset; @@ -257,21 +255,21 @@ protected: bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - + bool _showTrueJointTransforms; class MeshState { public: QVector clusterMatrices; }; - + QVector _meshStates; - + // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); virtual void initJointStates(QVector states); - + void setScaleInternal(const glm::vec3& scale); void scaleToFit(); void snapToRegistrationPoint(); @@ -298,11 +296,11 @@ protected: /// the original position /// \return true if the joint was found bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f); - + /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's /// first free ancestor. float getLimbLength(int jointIndex) const; - + /// Allow sub classes to force invalidating the bboxes void invalidCalculatedMeshBoxes() { _calculatedMeshBoxesValid = false; @@ -316,28 +314,25 @@ protected: // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; -protected slots: void geometryRefreshed(); - + private: - - friend class AnimationHandle; - + void applyNextGeometry(); void deleteGeometry(); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); - + QSharedPointer _nextGeometry; float _lodDistance; float _lodHysteresis; float _nextLODHysteresis; QSharedPointer _collisionGeometry; - + float _pupilDilation; QVector _blendshapeCoefficients; - + QUrl _url; QUrl _collisionUrl; bool _isVisible; @@ -347,10 +342,6 @@ private: gpu::Batch _renderBatch; QVector > > _dilatedTextures; - - QSet _animationHandles; - - QList _runningAnimations; QVector _blendedBlendshapeCoefficients; int _blendNumber; @@ -375,12 +366,12 @@ private: QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes QHash, qint64> _calculatedMeshPartOffset; bool _calculatedMeshPartOffsetValid; - - + + bool _calculatedMeshPartBoxesValid; QVector _calculatedMeshBoxes; // world coordinate AABoxes for all sub mesh boxes bool _calculatedMeshBoxesValid; - + QVector< QVector > _calculatedMeshTriangles; // world coordinate triangles for all sub meshes bool _calculatedMeshTrianglesValid; QMutex _mutex; @@ -420,10 +411,10 @@ private: IS_SHADOW_FLAG, IS_MIRROR_FLAG, //THis means that the mesh is rendered mirrored, not the same as "Rear view mirror" IS_WIREFRAME_FLAG, - + NUM_FLAGS, }; - + enum Flag { IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG), HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG), @@ -489,7 +480,7 @@ private: RenderKey(int bitmask) : _flags(bitmask) {} }; - + class RenderPipeline { public: gpu::PipelinePointer _pipeline; @@ -503,7 +494,7 @@ private: public: typedef RenderKey Key; - + void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader); void initLocations(gpu::ShaderPointer& program, Locations& locations); @@ -511,8 +502,8 @@ private: static RenderPipelineLib _renderPipelineLib; bool _renderCollisionHull; - - + + QSet> _transparentRenderItems; QSet> _opaqueRenderItems; QMap _renderItems; From 9593668110ee4971971ee006b0df244619e1b60c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Jul 2015 15:08:22 -0700 Subject: [PATCH 018/109] Bring animation file headers up to date. --- libraries/animation/src/AnimationCache.cpp | 2 +- libraries/animation/src/AnimationCache.h | 2 +- libraries/animation/src/AnimationHandle.cpp | 2 +- libraries/animation/src/AnimationHandle.h | 2 +- libraries/animation/src/AnimationLoop.cpp | 2 +- libraries/animation/src/AnimationLoop.h | 2 +- libraries/animation/src/AnimationObject.cpp | 2 +- libraries/animation/src/AnimationObject.h | 2 +- libraries/animation/src/JointState.cpp | 2 +- libraries/animation/src/JointState.h | 2 +- libraries/animation/src/Rig.cpp | 2 +- libraries/animation/src/Rig.h | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 99224f7dce..fef20b3cdb 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -1,6 +1,6 @@ // // AnimationCache.cpp -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/14/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index c90c4c9225..840d7a0355 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -1,6 +1,6 @@ // // AnimationCache.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/14/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index d11dcacfc6..996b8cb1fb 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -1,6 +1,6 @@ // // AnimationHandle.cpp -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. diff --git a/libraries/animation/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h index a8c9d800a4..9075118f43 100644 --- a/libraries/animation/src/AnimationHandle.h +++ b/libraries/animation/src/AnimationHandle.h @@ -1,6 +1,6 @@ // // AnimationHandle.h -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. diff --git a/libraries/animation/src/AnimationLoop.cpp b/libraries/animation/src/AnimationLoop.cpp index 43e049f851..a2a27170c2 100644 --- a/libraries/animation/src/AnimationLoop.cpp +++ b/libraries/animation/src/AnimationLoop.cpp @@ -1,6 +1,6 @@ // // AnimationLoop.cpp -// libraries/animation +// libraries/animation/src/ // // Created by Brad Hefta-Gaub on 11/12/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationLoop.h b/libraries/animation/src/AnimationLoop.h index d4537c4656..02161544ba 100644 --- a/libraries/animation/src/AnimationLoop.h +++ b/libraries/animation/src/AnimationLoop.h @@ -1,6 +1,6 @@ // // AnimationLoop.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Brad Hefta-Gaub on 11/12/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index ede1e82623..25a5743121 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -1,6 +1,6 @@ // // AnimationObject.cpp -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/17/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index 078fc31fb3..aa69e78ceb 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -1,6 +1,6 @@ // // AnimationObject.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Andrzej Kapolka on 4/17/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index a82a57f0ed..8f14342e80 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -1,6 +1,6 @@ // // JointState.cpp -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index 0ef84e50c4..f15590e5f2 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -1,6 +1,6 @@ // // JointState.h -// interface/src/renderer +// libraries/animation/src/ // // Created by Andrzej Kapolka on 10/18/13. // Copyright 2013 High Fidelity, Inc. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0a889fce88..4466bdbe26 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1,6 +1,6 @@ // // Rig.cpp -// libraries/script-engine/src/ +// libraries/animation/src/ // // Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e60116a35e..7e209841a1 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -1,6 +1,6 @@ // // Rig.h -// libraries/script-engine/src/ +// libraries/animation/src/ // // Produces animation data and hip placement for the current timestamp. // From 81e0a1e629be09cdd811225e40a6399bf32b7d40 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Jul 2015 15:09:06 -0700 Subject: [PATCH 019/109] Better name for signal. --- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 942dbeeaf7..aab760810f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1087,7 +1087,7 @@ void AvatarData::setJointMappingsFromNetworkReply() { } networkReply->deleteLater(); - emit jointsLoaded(); + emit jointMappingLoaded(); } void AvatarData::sendAvatarDataPacket() { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 285460651a..60c643eff9 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -313,7 +313,7 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } signals: - void jointsLoaded(); // So that test cases or anyone waiting on asynchronous loading can be informed. + void jointMappingLoaded(); // So that test cases or anyone waiting on asynchronous loading can be informed. public slots: void sendAvatarDataPacket(); From eea3ce4369237b32be6d61993057c9e5588709d6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Jul 2015 15:11:21 -0700 Subject: [PATCH 020/109] Make an actual rig in the test case. --- tests/rig/src/RigTests.cpp | 61 +++++++++++++++++++++++++++----------- tests/rig/src/RigTests.h | 6 ++-- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index 37f7a2bbab..100d70ed25 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -40,30 +40,55 @@ */ #include -//#include "FSTReader.h" -// There are two good ways we could organize this: -// 1. Create a MyAvatar the same way that Interface does, and poke at it. -// We can't do that because MyAvatar (and even Avatar) are in interface, not a library, and our build system won't allow that dependency. -// 2. Create just the minimum skeleton in the most direct way possible, using only very basic library APIs (such as fbx). -// I don't think we can do that because not everything we need is exposed directly from, e.g., the fst and fbx readers. -// So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and -// duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path. +#include + #include "AvatarData.h" +#include "OBJReader.h" +#include "FBXReader.h" + +#include "AvatarRig.h" // We might later test Rig vs AvatarRig separately, but for now, we're concentrating on the main use case. #include "RigTests.h" QTEST_MAIN(RigTests) void RigTests::initTestCase() { - AvatarData avatar; - QEventLoop loop; // Create an event loop that will quit when we get the finished signal - QObject::connect(&avatar, &AvatarData::jointsLoaded, &loop, &QEventLoop::quit); - avatar.setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach - //std::cout << "sleep start" << std::endl; - loop.exec(); // Nothing is going to happen on this whole run thread until we get this - _rig = new Rig(); -} -void RigTests::dummyPassTest() { + // There are two good ways we could organize this: + // 1. Create a MyAvatar the same way that Interface does, and poke at it. + // We can't do that because MyAvatar (and even Avatar) are in interface, not a library, and our build system won't allow that dependency. + // 2. Create just the minimum skeleton in the most direct way possible, using only very basic library APIs (such as fbx). + // I don't think we can do that because not everything we need is exposed directly from, e.g., the fst and fbx readers. + // So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and + // duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path. + + // Joint mapping from fst + auto avatar = std::make_shared(); + QEventLoop loop; // Create an event loop that will quit when we get the finished signal + QObject::connect(avatar.get(), &AvatarData::jointMappingLoaded, &loop, &QEventLoop::quit); + avatar->setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach fst + loop.exec(); // Blocking all further tests until signalled. + + // Joint geometry from fbx. + QUrl fbxUrl("https://s3.amazonaws.com/hifi-public/models/skeletons/Zack/Zack.fbx"); + QNetworkReply* netReply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request + QCOMPARE(netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200), true); + FBXGeometry geometry = readFBX(netReply->readAll(), QVariantHash()); + QCOMPARE(geometry.joints.count(), avatar->getJointNames().count()); + + QVector jointStates; + for (int i = 0; i < geometry.joints.size(); ++i) { + const FBXJoint& joint = geometry.joints[i]; + JointState state; + state.setFBXJoint(&joint); + jointStates.append(state); + } + + _rig = std::make_shared(); + _rig->initJointStates(jointStates, glm::mat4()); + std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; + } + +/*void RigTests::dummyPassTest() { bool x = true; std::cout << "dummyPassTest x=" << x << std::endl; QCOMPARE(x, true); @@ -73,4 +98,4 @@ void RigTests::dummyFailTest() { bool x = false; std::cout << "dummyFailTest x=" << x << std::endl; QCOMPARE(x, true); -} +}*/ diff --git a/tests/rig/src/RigTests.h b/tests/rig/src/RigTests.h index 9f8ba22eb8..1ce692b858 100644 --- a/tests/rig/src/RigTests.h +++ b/tests/rig/src/RigTests.h @@ -46,11 +46,11 @@ class RigTests : public QObject { private slots: void initTestCase(); - void dummyPassTest(); - void dummyFailTest(); + /*void dummyPassTest(); + void dummyFailTest();*/ private: - Rig* _rig; + RigPointer _rig; }; #endif // hifi_RigTests_h From 33c97a18337fdbf46a5713aad8a03bf7ca882c93 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 23 Jul 2015 15:14:10 -0700 Subject: [PATCH 021/109] get rid of _firstPersonSkeletonModel in MyAvatar. add flag in Rig for joints being dirty so Model knows when to recompute meshes --- interface/src/avatar/MyAvatar.cpp | 25 +----- interface/src/avatar/MyAvatar.h | 2 - interface/src/avatar/SkeletonModel.cpp | 90 ++++++++------------- interface/src/avatar/SkeletonModel.h | 7 +- libraries/animation/src/AnimationHandle.cpp | 6 +- libraries/animation/src/Rig.cpp | 89 ++++++++++++++++---- libraries/animation/src/Rig.h | 31 +++++-- libraries/render-utils/src/Model.cpp | 24 +----- libraries/render-utils/src/Model.h | 3 - 9 files changed, 137 insertions(+), 140 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 178ebc0487..d46870b479 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -103,11 +103,8 @@ MyAvatar::MyAvatar(RigPointer rig) : _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _rig(rig), - _firstPersonSkeletonModel(this, nullptr, rig), _prevShouldDrawHead(true) { - _firstPersonSkeletonModel.setIsFirstPerson(true); - ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -141,7 +138,6 @@ QByteArray MyAvatar::toByteArray() { void MyAvatar::reset() { _skeletonModel.reset(); - _firstPersonSkeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -200,7 +196,6 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); - _firstPersonSkeletonModel.simulate(deltaTime); } if (!_skeletonModel.hasSkeleton()) { @@ -1028,15 +1023,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (_useFullAvatar) { _skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene); - - const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst"); - _firstPersonSkeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); - _firstPersonSkeletonModel.setVisibleInScene(!_prevShouldDrawHead, scene); } else { _skeletonModel.setVisibleInScene(true, scene); - - _firstPersonSkeletonModel.setVisibleInScene(false, scene); - _firstPersonSkeletonModel.reset(); } } @@ -1254,23 +1242,14 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { const bool shouldDrawHead = shouldRenderHead(renderArgs); _skeletonModel.initWhenReady(scene); - if (_useFullAvatar) { - _firstPersonSkeletonModel.initWhenReady(scene); - } if (shouldDrawHead != _prevShouldDrawHead) { if (_useFullAvatar) { - if (shouldDrawHead) { - _skeletonModel.setVisibleInScene(true, scene); - _firstPersonSkeletonModel.setVisibleInScene(false, scene); - } else { - _skeletonModel.setVisibleInScene(false, scene); - _firstPersonSkeletonModel.setVisibleInScene(true, scene); - } + _skeletonModel.setVisibleInScene(true, scene); + _rig->setFirstPerson(!shouldDrawHead); } else { getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene); } - } _prevShouldDrawHead = shouldDrawHead; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0c598c21be..c6cb48878f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -287,8 +287,6 @@ private: QString _fullAvatarModelName; RigPointer _rig; - // used for rendering when in first person view or when in an HMD. - SkeletonModel _firstPersonSkeletonModel; bool _prevShouldDrawHead; }; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2d9db1abb7..0ee3898a20 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -40,8 +40,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r _standingFoot(NO_FOOT), _standingOffset(0.0f), _clampedFootPosition(0.0f), - _headClipDistance(DEFAULT_NEAR_CLIP), - _isFirstPerson(false) + _headClipDistance(DEFAULT_NEAR_CLIP) { assert(_rig); assert(_owningAvatar); @@ -54,7 +53,7 @@ SkeletonModel::~SkeletonModel() { void SkeletonModel::initJointStates(QVector states) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _boundingRadius = _rig->initJointStates(states, parentTransform); + _boundingRadius = _rig->initJointStates(states, parentTransform, geometry.neckJointIndex); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; @@ -98,6 +97,29 @@ void SkeletonModel::initJointStates(QVector states) { const float PALM_PRIORITY = DEFAULT_PRIORITY; const float LEAN_PRIORITY = DEFAULT_PRIORITY; + +void SkeletonModel::updateClusterMatrices() { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::mat4 modelToWorld = glm::mat4_cast(_rotation); + for (int i = 0; i < _meshStates.size(); i++) { + MeshState& state = _meshStates[i]; + const FBXMesh& mesh = geometry.meshes.at(i); + if (_showTrueJointTransforms) { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = + modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; + } + } else { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = + modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; + } + } + } +} + void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getSkeletonPosition()); static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -154,8 +176,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } } - if (_isFirstPerson) { - cauterizeHead(); + // if (_isFirstPerson) { + // cauterizeHead(); + // updateClusterMatrices(); + // } + if (_rig->getJointsAreDirty()) { updateClusterMatrices(); } @@ -764,7 +789,7 @@ void SkeletonModel::resetShapePositionsToDefaultPose() { // geometry or joints have not yet been created return; } - + const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (geometry.joints.isEmpty()) { return; @@ -820,7 +845,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha // draw a green cylinder between the two points glm::vec3 origin(0.0f); - Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), + Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } @@ -828,56 +853,5 @@ bool SkeletonModel::hasSkeleton() { return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; } -void SkeletonModel::initHeadBones() { - _headBones.clear(); - const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); - const int neckJointIndex = fbxGeometry.neckJointIndex; - std::queue q; - q.push(neckJointIndex); - _headBones.push_back(neckJointIndex); - - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. - while (q.size() > 0) { - int jointIndex = q.front(); - for (int i = 0; i < fbxGeometry.joints.size(); i++) { - const FBXJoint& fbxJoint = fbxGeometry.joints[i]; - if (jointIndex == fbxJoint.parentIndex) { - _headBones.push_back(i); - q.push(i); - } - } - q.pop(); - } -} - -void SkeletonModel::invalidateHeadBones() { - _headBones.clear(); -} - -void SkeletonModel::cauterizeHead() { - if (isActive()) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const int neckJointIndex = geometry.neckJointIndex; - if (neckJointIndex > 0 && neckJointIndex < _rig->getJointStateCount()) { - - // lazy init of headBones - if (_headBones.size() == 0) { - initHeadBones(); - } - - // preserve the translation for the neck - // glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3]; - glm::vec4 trans = _rig->getJointTransform(neckJointIndex)[3]; - glm::vec4 zero(0, 0, 0, 0); - for (const int &i : _headBones) { - glm::mat4 newXform(zero, zero, zero, trans); - _rig->setJointTransform(i, newXform); - _rig->setJointVisibleTransform(i, newXform); - } - } - } -} - void SkeletonModel::onInvalidate() { - invalidateHeadBones(); } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index c4cd43b4df..0678ae48d4 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -112,9 +112,6 @@ public: float getHeadClipDistance() const { return _headClipDistance; } - void setIsFirstPerson(bool value) { _isFirstPerson = value; } - bool getIsFirstPerson() const { return _isFirstPerson; } - virtual void onInvalidate() override; signals: @@ -138,6 +135,7 @@ protected: void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index); + void updateClusterMatrices(); void cauterizeHead(); void initHeadBones(); void invalidateHeadBones(); @@ -173,9 +171,6 @@ private: glm::vec3 _clampedFootPosition; float _headClipDistance; // Near clip distance to use if no separate head model - - bool _isFirstPerson; - std::vector _headBones; }; #endif // hifi_SkeletonModel_h diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index d11dcacfc6..8978abd93c 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -176,8 +176,7 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState state = _rig->getJointState(mapping); - if (_priority == state._animationPriority) { + if (_priority == _rig->getJointAnimatinoPriority(mapping)) { _rig->setJointAnimatinoPriority(mapping, newPriority); } } @@ -188,8 +187,7 @@ void AnimationHandle::restoreJoints() { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { - JointState state = _rig->getJointState(mapping); - _rig->restoreJointRotation(mapping, 1.0f, state._animationPriority); + _rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); } } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0a889fce88..d42f65df32 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "AnimationHandle.h" #include "Rig.h" @@ -48,9 +50,9 @@ void Rig::deleteAnimations() { } } - -float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { +float Rig::initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex) { _jointStates = states; + _neckJointIndex = neckJointIndex; initJointTransforms(parentTransform); int numStates = _jointStates.size(); @@ -66,6 +68,8 @@ float Rig::initJointStates(QVector states, glm::mat4 parentTransform _jointStates[i].slaveVisibleTransform(); } + initHeadBones(); + return radius; } @@ -106,7 +110,8 @@ JointState Rig::getJointState(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return JointState(); } - return _jointStates[jointIndex]; + // return _jointStates[jointIndex]; + return maybeCauterizeHead(jointIndex); } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { @@ -144,6 +149,13 @@ void Rig::clearJointAnimationPriority(int index) { } } +float Rig::getJointAnimatinoPriority(int index) { + if (index != -1 && index < _jointStates.size()) { + return _jointStates[index]._animationPriority; + } + return 0.0f; +} + void Rig::setJointAnimatinoPriority(int index, float newPriority) { if (index != -1 && index < _jointStates.size()) { _jointStates[index]._animationPriority = newPriority; @@ -173,7 +185,8 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, return false; } // position is in world-frame - position = translation + rotation * _jointStates[jointIndex].getPosition(); + // position = translation + rotation * _jointStates[jointIndex].getPosition(); + position = translation + rotation * maybeCauterizeHead(jointIndex).getPosition(); return true; } @@ -182,7 +195,7 @@ bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { return false; } // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); + position = extractTranslation(maybeCauterizeHead(jointIndex).getTransform()); return true; } @@ -190,7 +203,7 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * _jointStates[jointIndex].getRotation(); + result = rotation * maybeCauterizeHead(jointIndex).getRotation(); return true; } @@ -198,7 +211,7 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = _jointStates[jointIndex].getRotation(); + rotation = maybeCauterizeHead(jointIndex).getRotation(); return true; } @@ -206,7 +219,7 @@ bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm: if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * _jointStates[jointIndex].getRotation(); + result = rotation * maybeCauterizeHead(jointIndex).getRotation(); return true; } @@ -217,7 +230,7 @@ bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& positio return false; } // position is in world-frame - position = translation + rotation * _jointStates[jointIndex].getVisiblePosition(); + position = translation + rotation * maybeCauterizeHead(jointIndex).getVisiblePosition(); return true; } @@ -225,7 +238,7 @@ bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * _jointStates[jointIndex].getVisibleRotation(); + result = rotation * maybeCauterizeHead(jointIndex).getVisibleRotation(); return true; } @@ -233,14 +246,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } - return _jointStates[jointIndex].getTransform(); + return maybeCauterizeHead(jointIndex).getTransform(); } glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } - return _jointStates[jointIndex].getVisibleTransform(); + return maybeCauterizeHead(jointIndex).getVisibleTransform(); } void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform) { @@ -492,7 +505,7 @@ glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { if (jointIndex == -1 || _jointStates.isEmpty()) { return glm::vec3(); } - return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); + return maybeCauterizeHead(jointIndex).getDefaultTranslationInConstrainedFrame(); } glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { @@ -537,5 +550,53 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { if (jointIndex == -1 || _jointStates.isEmpty()) { return glm::quat(); } - return _jointStates[jointIndex].getDefaultRotationInParentFrame(); + return maybeCauterizeHead(jointIndex).getDefaultRotationInParentFrame(); +} + +void Rig::initHeadBones() { + if (_neckJointIndex == -1) { + return; + } + _headBones.clear(); + std::queue q; + q.push(_neckJointIndex); + _headBones.push_back(_neckJointIndex); + + // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + while (q.size() > 0) { + int jointIndex = q.front(); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint(); + if (jointIndex == fbxJoint.parentIndex) { + _headBones.push_back(i); + q.push(i); + } + } + q.pop(); + } +} + +JointState Rig::maybeCauterizeHead(int jointIndex) const { + // if (_headBones.contains(jointIndex)) { + // XXX fix this... make _headBones a hash? add a flag to JointState? + if (_neckJointIndex != -1 && + _isFirstPerson && + std::find(_headBones.begin(), _headBones.end(), jointIndex) != _headBones.end()) { + glm::vec4 trans = _jointStates[jointIndex].getTransform()[3]; + glm::vec4 zero(0, 0, 0, 0); + glm::mat4 newXform(zero, zero, zero, trans); + JointState jointStateCopy = _jointStates[jointIndex]; + jointStateCopy.setTransform(newXform); + jointStateCopy.setVisibleTransform(newXform); + return jointStateCopy; + } else { + return _jointStates[jointIndex]; + } +} + +void Rig::setFirstPerson(bool isFirstPerson) { + if (_isFirstPerson != isFirstPerson) { + _isFirstPerson = isFirstPerson; + _jointsAreDirty = true; + } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e60116a35e..1073bd6dab 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -11,14 +11,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* TBD: - - What iare responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? + - What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? Is there common/copied code (e.g., ScriptableAvatar::update)? - - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics? - - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly? + - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object + physics? + - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the + system choosing randomly? - Distribute some doc from here to the right files if it turns out to be correct: - - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity. - But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject is to AnimationPointer? + - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything + equivalent to editEntity. + But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject + is to AnimationPointer? */ #ifndef __hifi__Rig__ @@ -51,7 +55,7 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } void deleteAnimations(); - float initJointStates(QVector states, glm::mat4 parentTransform); + float initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; int getJointStateCount() const { return _jointStates.size(); } @@ -60,11 +64,12 @@ public: void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); - JointState getJointState(int jointIndex) const; + JointState getJointState(int jointIndex) const; // XXX bool getVisibleJointState(int index, glm::quat& rotation) const; void clearJointState(int index); void clearJointStates(); void clearJointAnimationPriority(int index); + float getJointAnimatinoPriority(int index); void setJointAnimatinoPriority(int index, float newPriority); void setJointState(int index, bool valid, const glm::quat& rotation, float priority); void restoreJointRotation(int index, float fraction, float priority); @@ -102,11 +107,23 @@ public: virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0; + virtual void setFirstPerson(bool isFirstPerson); + virtual bool getIsFirstPerson() const { return _isFirstPerson; } + + bool getJointsAreDirty() { return _jointsAreDirty; } + protected: QVector _jointStates; QSet _animationHandles; QList _runningAnimations; + + JointState maybeCauterizeHead(int jointIndex) const; + void initHeadBones(); + bool _isFirstPerson = false; + std::vector _headBones; + bool _jointsAreDirty = false; + int _neckJointIndex = -1; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index bd33e75207..c769f6bbe5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -478,7 +478,7 @@ bool Model::updateGeometry() { void Model::initJointStates(QVector states) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _boundingRadius = _rig->initJointStates(states, parentTransform); + _boundingRadius = _rig->initJointStates(states, parentTransform, geometry.neckJointIndex); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -1341,28 +1341,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) { } } -void Model::updateClusterMatrices() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); - for (int i = 0; i < _meshStates.size(); i++) { - MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); - if (_showTrueJointTransforms) { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; - } - } else { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; - } - } - } -} - void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 30c5211990..d7178a389a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -308,9 +308,6 @@ protected: _calculatedMeshTrianglesValid = false; } - // rebuild the clusterMatrices from the current jointStates - void updateClusterMatrices(); - // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; From cc26f5165bba9714d12edfc9e1eeedf2b0a30fa8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Jul 2015 15:40:33 -0700 Subject: [PATCH 022/109] Supply neck index per new protocol. --- tests/rig/src/RigTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index 100d70ed25..95ddc86b3f 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -84,7 +84,7 @@ void RigTests::initTestCase() { } _rig = std::make_shared(); - _rig->initJointStates(jointStates, glm::mat4()); + _rig->initJointStates(jointStates, glm::mat4(), geometry.neckJointIndex); std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; } From 4e298d815ddbb5ebcf7c81391454dffb1a33c13c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 24 Jul 2015 14:07:32 -0700 Subject: [PATCH 023/109] Add the joint mapping necessary for playing animations in the rig. --- libraries/animation/src/AnimationHandle.cpp | 11 ++++++++++- libraries/animation/src/AnimationHandle.h | 4 ++-- libraries/animation/src/Rig.cpp | 11 +++++++++++ libraries/animation/src/Rig.h | 11 ++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index 51e8cb62d2..e8ac6e0a10 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -103,6 +103,15 @@ void AnimationHandle::setJointMappings(QVector jointMappings) { _jointMappings = jointMappings; } +QVector AnimationHandle::getJointMappings() { + if (_jointMappings.isEmpty()) { + QVector animationJoints = _animation->getGeometry().joints; + for (int i = 0; i < animationJoints.count(); i++) { + _jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name)); + } + } + return _jointMappings; +} void AnimationHandle::simulate(float deltaTime) { if (!_animation || !_animation->isLoaded()) { @@ -111,7 +120,7 @@ void AnimationHandle::simulate(float deltaTime) { _animationLoop.simulate(deltaTime); - if (_jointMappings.isEmpty()) { + if (getJointMappings().isEmpty()) { qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()"; return; } diff --git a/libraries/animation/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h index 9075118f43..5d39682a0a 100644 --- a/libraries/animation/src/AnimationHandle.h +++ b/libraries/animation/src/AnimationHandle.h @@ -45,7 +45,7 @@ inline uint qHash(const std::weak_ptr& a, uint seed) { -/// Represents a handle to a model animation. +/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig. class AnimationHandle : public QObject, public std::enable_shared_from_this { Q_OBJECT @@ -96,6 +96,7 @@ public: void setAnimationDetails(const AnimationDetails& details); void setJointMappings(QVector jointMappings); + QVector getJointMappings(); // computing if necessary void simulate(float deltaTime); void applyFrame(float frameIndex); void replaceMatchingPriorities(float newPriority); @@ -125,7 +126,6 @@ private: AnimationLoop _animationLoop; static QHash, QVector> _jointMappingsCache; - static QVector getJointMappings(const AnimationPointer& animation); }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e0b1625813..8f4047bc71 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -73,6 +73,17 @@ float Rig::initJointStates(QVector states, glm::mat4 parentTransform return radius; } +// We could build and cache a dictionary, too.... +// Should we be using .fst mapping instead/also? +int Rig::indexOfJoint(const QString& jointName) { + for (int i = 0; i < _jointStates.count(); i++) { + if (_jointStates[i].getFBXJoint().name == jointName) { + return i; + } + } + return -1; +} + void Rig::initJointTransforms(glm::mat4 parentTransform) { // compute model transforms diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 20110813fc..0af4073c96 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -10,7 +10,15 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* TBD: +/* + Things we want to be able to do, that I think we cannot do now: + * Stop an animation at a given time so that it can be examined visually or in a test harness. (I think we can already stop animation and set frame to a computed float? But does that move the bones?) + * Play two animations, blending between them. (Current structure just has one, under script control.) + * Fade in an animation over another. + * Apply IK, lean, head pointing or other overrides relative to previous position. + All of this depends on coordinated state. + + TBD: - What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? Is there common/copied code (e.g., ScriptableAvatar::update)? - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object @@ -58,6 +66,7 @@ public: float initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; int getJointStateCount() const { return _jointStates.size(); } + int indexOfJoint(const QString& jointName) ; void initJointTransforms(glm::mat4 parentTransform); void clearJointTransformTranslation(int jointIndex); From cff7bb703e8b397dc88c25efd363a440d21744b3 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 24 Jul 2015 14:08:30 -0700 Subject: [PATCH 024/109] Test evolution. --- tests/rig/src/RigTests.cpp | 48 ++++++++++++++++++++++++-------------- tests/rig/src/RigTests.h | 3 +-- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index 95ddc86b3f..c5622ac501 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -61,25 +61,34 @@ void RigTests::initTestCase() { // So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and // duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path. - // Joint mapping from fst - auto avatar = std::make_shared(); + // Joint mapping from fst. FIXME: Do we need this??? + /*auto avatar = std::make_shared(); QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(avatar.get(), &AvatarData::jointMappingLoaded, &loop, &QEventLoop::quit); avatar->setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach fst - loop.exec(); // Blocking all further tests until signalled. + loop.exec();*/ // Blocking all further tests until signalled. // Joint geometry from fbx. +#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" +#ifdef FROM_FILE + QFile file(FROM_FILE); + QCOMPARE(file.open(QIODevice::ReadOnly), true); + FBXGeometry geometry = readFBX(file.readAll(), QVariantHash()); +#else QUrl fbxUrl("https://s3.amazonaws.com/hifi-public/models/skeletons/Zack/Zack.fbx"); - QNetworkReply* netReply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request - QCOMPARE(netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200), true); - FBXGeometry geometry = readFBX(netReply->readAll(), QVariantHash()); - QCOMPARE(geometry.joints.count(), avatar->getJointNames().count()); + QNetworkReply* reply = OBJReader().request(fbxUrl, false); // Just a convenience hack for synchronoud http request + auto fbxHttpCode = !reply->isFinished() ? -1 : reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QCOMPARE(fbxHttpCode, 200); + FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash()); +#endif + //QCOMPARE(geometry.joints.count(), avatar->getJointNames().count()); QVector jointStates; for (int i = 0; i < geometry.joints.size(); ++i) { - const FBXJoint& joint = geometry.joints[i]; + // Note that if the geometry is stack allocated and goes away, so will the joints. Hence the heap copy here. + FBXJoint* joint = new FBXJoint(geometry.joints[i]); JointState state; - state.setFBXJoint(&joint); + state.setFBXJoint(joint); jointStates.append(state); } @@ -88,14 +97,17 @@ void RigTests::initTestCase() { std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; } -/*void RigTests::dummyPassTest() { - bool x = true; - std::cout << "dummyPassTest x=" << x << std::endl; - QCOMPARE(x, true); +void reportJoint(int index, JointState joint) { // Handy for debugging + std::cout << "\n"; + std::cout << index << " " << joint.getFBXJoint().name.toUtf8().data() << "\n"; + std::cout << " pos:" << joint.getPosition() << "/" << joint.getPositionInParentFrame() << " from " << joint.getParentIndex() << "\n"; + std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n"; + std::cout << "\n"; } -void RigTests::dummyFailTest() { - bool x = false; - std::cout << "dummyFailTest x=" << x << std::endl; - QCOMPARE(x, true); -}*/ +void RigTests::initialPoseArmsDown() { + for (int i = 0; i < _rig->getJointStateCount(); i++) { + JointState joint = _rig->getJointState(i); + reportJoint(i, joint); + } +} diff --git a/tests/rig/src/RigTests.h b/tests/rig/src/RigTests.h index 1ce692b858..4280c0a8fa 100644 --- a/tests/rig/src/RigTests.h +++ b/tests/rig/src/RigTests.h @@ -46,8 +46,7 @@ class RigTests : public QObject { private slots: void initTestCase(); - /*void dummyPassTest(); - void dummyFailTest();*/ + void initialPoseArmsDown(); private: RigPointer _rig; From 1d99057052c82b88764a83f575b7c94409aeb34f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 24 Jul 2015 16:08:47 -0700 Subject: [PATCH 025/109] Debug printouts in tests. --- tests/rig/src/RigTests.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index c5622ac501..74047c0162 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -104,10 +104,24 @@ void reportJoint(int index, JointState joint) { // Handy for debugging std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n"; std::cout << "\n"; } - -void RigTests::initialPoseArmsDown() { - for (int i = 0; i < _rig->getJointStateCount(); i++) { - JointState joint = _rig->getJointState(i); +void reportByName(RigPointer rig, const QString& name) { + int jointIndex = rig->indexOfJoint(name); + reportJoint(jointIndex, rig->getJointState(jointIndex)); +} +void reportAll(RigPointer rig) { + for (int i = 0; i < rig->getJointStateCount(); i++) { + JointState joint = rig->getJointState(i); reportJoint(i, joint); } } +void reportSome(RigPointer rig) { + QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"}; + for (auto name : names) { + reportByName(rig, name); + } +} + +void RigTests::initialPoseArmsDown() { + //reportAll(_rig); + reportSome(_rig); +} From 05dda1220e54d856fc52ecb445331480aff9b888 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 24 Jul 2015 22:01:55 -0700 Subject: [PATCH 026/109] Add logging. --- libraries/animation/src/AnimationLogging.cpp | 14 ++++++++++++++ libraries/animation/src/AnimationLogging.h | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 libraries/animation/src/AnimationLogging.cpp create mode 100644 libraries/animation/src/AnimationLogging.h diff --git a/libraries/animation/src/AnimationLogging.cpp b/libraries/animation/src/AnimationLogging.cpp new file mode 100644 index 0000000000..11ed5ebd35 --- /dev/null +++ b/libraries/animation/src/AnimationLogging.cpp @@ -0,0 +1,14 @@ +// +// AnimationLogging.cpp +// libraries/audio/src +// +// Created by Howard Stearns on 7/24/15. +// Copyright 2014 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 "AnimationLogging.h" + +Q_LOGGING_CATEGORY(animation, "hifi.animation") diff --git a/libraries/animation/src/AnimationLogging.h b/libraries/animation/src/AnimationLogging.h new file mode 100644 index 0000000000..6c56e2dbe4 --- /dev/null +++ b/libraries/animation/src/AnimationLogging.h @@ -0,0 +1,18 @@ +// +// AnimationLogging.h +// libraries/animation/src/ +// +// Created by Howard Stearns on 7/24/15. +// +// 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_AnimationLogging_h +#define hifi_AnimationLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(animation) + +#endif From 8b5f24e4df09515c5a58f9515d6c00fc3eb5cdd3 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 24 Jul 2015 22:02:39 -0700 Subject: [PATCH 027/109] Keep list of animations in Rig, not MyAvatar. --- interface/src/avatar/MyAvatar.cpp | 92 +++++++------------------------ interface/src/avatar/MyAvatar.h | 8 +-- libraries/animation/src/Rig.cpp | 87 +++++++++++++++++++++++++++-- libraries/animation/src/Rig.h | 13 ++++- 4 files changed, 118 insertions(+), 82 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00fce3a1e7..655af63c55 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -491,17 +491,6 @@ void MyAvatar::loadLastRecording() { _player->loadRecording(_recorder->getRecording()); } -AnimationHandlePointer MyAvatar::addAnimationHandle() { - AnimationHandlePointer handle = _rig->createAnimationHandle(); - _animationHandles.append(handle); - return handle; -} - -void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) { - handle->stop(); - _animationHandles.removeOne(handle); -} - void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { if (QThread::currentThread() != thread()) { @@ -510,16 +499,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - AnimationHandlePointer handle = _rig->createAnimationHandle(); - handle->setURL(url); - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); + _rig->startAnimation(url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); } void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, @@ -530,25 +510,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } - // check for a configured animation for the role - foreach (const AnimationHandlePointer& handle, _animationHandles) { - if (handle->getRole() == role) { - handle->start(); - return; - } - } - // no joy; use the parameters provided - AnimationHandlePointer handle = _rig->createAnimationHandle(); - handle->setRole(role); - handle->setURL(url); - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); + _rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); } void MyAvatar::stopAnimationByRole(const QString& role) { @@ -556,11 +518,7 @@ void MyAvatar::stopAnimationByRole(const QString& role) { QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role)); return; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { - if (handle->getRole() == role) { - handle->stop(); - } - } + _rig->stopAnimationByRole(role); } void MyAvatar::stopAnimation(const QString& url) { @@ -568,11 +526,7 @@ void MyAvatar::stopAnimation(const QString& url) { QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); return; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { - if (handle->getURL() == url) { - handle->stop(); - } - } + _rig->stopAnimation(url); } AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { @@ -583,7 +537,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { Q_ARG(const QString&, role)); return result; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { + foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { if (handle->getRole() == role) { result = handle->getAnimationDetails(); break; @@ -600,7 +554,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) { Q_ARG(const QString&, url)); return result; } - foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { + foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { if (handle->getURL() == url) { result = handle->getAnimationDetails(); break; @@ -646,9 +600,11 @@ void MyAvatar::saveData() { settings.endArray(); settings.beginWriteArray("animationHandles"); - for (int i = 0; i < _animationHandles.size(); i++) { + auto animationHandles = _rig->getAnimationHandles(); + for (int i = 0; i < animationHandles.size(); i++) { settings.setArrayIndex(i); - const AnimationHandlePointer& pointer = _animationHandles.at(i); + const AnimationHandlePointer& pointer = animationHandles.at(i); + qCDebug(interfaceapp) << "Save animation" << pointer->getURL().toString(); settings.setValue("role", pointer->getRole()); settings.setValue("url", pointer->getURL()); settings.setValue("fps", pointer->getFPS()); @@ -766,25 +722,19 @@ void MyAvatar::loadData() { setAttachmentData(attachmentData); int animationCount = settings.beginReadArray("animationHandles"); - while (_animationHandles.size() > animationCount) { - _animationHandles.takeLast()->stop(); - } - while (_animationHandles.size() < animationCount) { - addAnimationHandle(); - } + _rig->deleteAnimations(); for (int i = 0; i < animationCount; i++) { settings.setArrayIndex(i); - const AnimationHandlePointer& handle = _animationHandles.at(i); - handle->setRole(settings.value("role", "idle").toString()); - handle->setURL(settings.value("url").toUrl()); - handle->setFPS(loadSetting(settings, "fps", 30.0f)); - handle->setPriority(loadSetting(settings, "priority", 1.0f)); - handle->setLoop(settings.value("loop", true).toBool()); - handle->setHold(settings.value("hold", false).toBool()); - handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat()); - handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat()); - handle->setMaskedJoints(settings.value("maskedJoints").toStringList()); - handle->setStartAutomatically(settings.value("startAutomatically", true).toBool()); + _rig->addAnimationByRole(settings.value("role", "idle").toString(), + settings.value("url").toString(), + loadSetting(settings, "fps", 30.0f), + loadSetting(settings, "priority", 1.0f), + settings.value("loop", true).toBool(), + settings.value("hold", false).toBool(), + settings.value("firstFrame", 0.0f).toFloat(), + settings.value("lastFrame", INT_MAX).toFloat(), + settings.value("maskedJoints").toStringList(), + settings.value("startAutomatically", true).toBool()); } settings.endArray(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c6cb48878f..802c92ec2c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -61,9 +61,9 @@ public: bool getShouldRenderLocally() const { return _shouldRender; } float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - const QList& getAnimationHandles() const { return _animationHandles; } - AnimationHandlePointer addAnimationHandle(); - void removeAnimationHandle(const AnimationHandlePointer& handle); + const QList& getAnimationHandles() const { return _rig->getAnimationHandles(); } + AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } + void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } /// Allows scripts to run animations. Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, @@ -256,8 +256,6 @@ private: bool _shouldRender; bool _billboardValid; float _oculusYawOffset; - - QList _animationHandles; bool _feetTouchFloor; eyeContactTarget _eyeContactTarget; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8f4047bc71..7d520e7930 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -12,7 +12,7 @@ #include #include "AnimationHandle.h" - +#include "AnimationLogging.h" #include "Rig.h" void insertSorted(QList& handles, const AnimationHandlePointer& handle) { @@ -27,9 +27,86 @@ void insertSorted(QList& handles, const AnimationHandleP AnimationHandlePointer Rig::createAnimationHandle() { AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); - _animationHandles.insert(handle); + _animationHandles.append(handle); return handle; } +void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { + handle->stop(); + // FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it. + _animationHandles.removeOne(handle); +} + +void Rig::startAnimation(const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints; + AnimationHandlePointer handle = nullptr; + foreach (const AnimationHandlePointer& candidate, _animationHandles) { + if (candidate->getURL() == url) { + handle = candidate; + break; + } + } + if (!handle) { + handle = createAnimationHandle(); + handle->setURL(url); + } + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + handle->start(); +} + +void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) { + // check for a configured animation for the role + qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; + AnimationHandlePointer handle = nullptr; + foreach (const AnimationHandlePointer& candidate, _animationHandles) { + if (candidate->getRole() == role) { + handle = candidate; + break; + } + } + if (!handle) { + handle = createAnimationHandle(); + handle->setRole(role); + } + handle->setURL(url); + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + if (startAutomatically) { + handle->start(); + } +} +void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true); +} + +void Rig::stopAnimationByRole(const QString& role) { + foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { + if (handle->getRole() == role) { + handle->stop(); + } + } +} + +void Rig::stopAnimation(const QString& url) { + foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { + if (handle->getURL() == url) { + handle->stop(); + } + } +} bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.removeOne(animationHandle); @@ -44,10 +121,10 @@ bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { } void Rig::deleteAnimations() { - for (QSet::iterator it = _animationHandles.begin(); it != _animationHandles.end(); ) { - (*it)->clearJoints(); - it = _animationHandles.erase(it); + for (auto animation : _animationHandles) { + removeAnimationHandle(animation); } + _animationHandles.clear(); } float Rig::initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 0af4073c96..2087ad6800 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -57,11 +57,22 @@ public: RigPointer getRigPointer() { return shared_from_this(); } AnimationHandlePointer createAnimationHandle(); + void removeAnimationHandle(const AnimationHandlePointer& handle); bool removeRunningAnimation(AnimationHandlePointer animationHandle); void addRunningAnimation(AnimationHandlePointer animationHandle); bool isRunningAnimation(AnimationHandlePointer animationHandle); const QList& getRunningAnimations() const { return _runningAnimations; } void deleteAnimations(); + const QList& getAnimationHandles() const { return _animationHandles; } + void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, + bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + void stopAnimation(const QString& url); + void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + void stopAnimationByRole(const QString& role); + void addAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically); float initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; @@ -124,7 +135,7 @@ public: protected: QVector _jointStates; - QSet _animationHandles; + QList _animationHandles; QList _runningAnimations; JointState maybeCauterizeHead(int jointIndex) const; From 8ac06c4059f696dcac5fec67a1241fbb52c2c079 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Jul 2015 08:48:16 -0700 Subject: [PATCH 028/109] remove friend class from Avatar --- interface/src/avatar/Avatar.h | 13 +++++++------ interface/src/avatar/AvatarManager.cpp | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index a587e69642..8ae4b9fb4d 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -186,9 +186,10 @@ public: virtual void computeShapeInfo(ShapeInfo& shapeInfo); - friend class AvatarManager; + void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } + AvatarMotionState* getMotionState() { return _motionState; } -signals: + signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); protected: @@ -218,7 +219,7 @@ protected: glm::vec3 _worldUpDirection; float _stringLength; bool _moving; ///< set when position is changing - + // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } @@ -243,7 +244,7 @@ protected: virtual void updateJointMappings(); render::ItemID _renderItemID; - + private: bool _initialized; NetworkTexturePointer _billboardTexture; @@ -251,9 +252,9 @@ private: bool _isLookAtTarget; void renderBillboard(RenderArgs* renderArgs); - + float getBillboardSize() const; - + static int _jointConesID; int _voiceSphereID; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ead330a41f..ee59a01e07 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -179,11 +179,11 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe // protected void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { auto rawPointer = std::static_pointer_cast(avatar); - AvatarMotionState* motionState= rawPointer->_motionState; + AvatarMotionState* motionState = rawPointer->getMotionState(); if (motionState) { // clean up physics stuff motionState->clearObjectBackPointer(); - rawPointer->_motionState = nullptr; + rawPointer->setMotionState(nullptr); _avatarMotionStates.remove(motionState); _motionStatesToAdd.remove(motionState); _motionStatesToDelete.push_back(motionState); @@ -307,7 +307,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { AvatarHash::iterator avatarItr = _avatarHash.find(id); if (avatarItr != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarItr.value()); - AvatarMotionState* motionState = avatar->_motionState; + AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); } else { @@ -316,7 +316,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); - avatar->_motionState = motionState; + avatar->setMotionState(motionState); _motionStatesToAdd.insert(motionState); _avatarMotionStates.insert(motionState); } From 7e582830415b351d40e3366ab4f77bf6b53b7194 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Jul 2015 08:54:08 -0700 Subject: [PATCH 029/109] change the methods that aren't called from outside the class to be private --- interface/src/avatar/MyAvatar.h | 131 +++++++++++++++----------------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 802c92ec2c..4fd66c0347 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -39,51 +39,40 @@ public: MyAvatar(RigPointer rig); ~MyAvatar(); - QByteArray toByteArray(); + void reset(); void update(float deltaTime); - void simulate(float deltaTime); void preRender(RenderArgs* renderArgs); - void updateFromTrackers(float deltaTime); - virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; - virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override; - virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; - - // setters void setLeanScale(float scale) { _leanScale = scale; } - void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } - // getters float getLeanScale() const { return _leanScale; } Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; - bool getShouldRenderLocally() const { return _shouldRender; } + float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - + const QList& getAnimationHandles() const { return _rig->getAnimationHandles(); } AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } - /// Allows scripts to run animations. Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, - bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - + bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); /// Stops an animation as identified by a URL. Q_INVOKABLE void stopAnimation(const QString& url); - + /// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom /// animation for the role. Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, - float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); /// Stops an animation identified by its role. Q_INVOKABLE void stopAnimationByRole(const QString& role); - Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); - + void clearJointAnimationPriorities(); + // get/set avatar data void saveData(); void loadData(); @@ -94,41 +83,32 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; - bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; - void relayDriveKeysToCharacterController(); - bool isMyAvatar() const { return true; } - eyeContactTarget getEyeContactTarget(); - virtual int parseDataFromBuffer(const QByteArray& buffer); - static void sendKillAvatar(); - + Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); } Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); } - + Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } - + Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } + AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); - - virtual void setJointRotations(QVector jointRotations); - virtual void setJointData(int index, const glm::quat& rotation); - virtual void clearJointData(int index); - virtual void clearJointsData(); Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString()); Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString()); - Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString()); + Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, + const QString& headName = QString(), const QString& bodyName = QString()); Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; } Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } @@ -143,48 +123,30 @@ public: virtual void setAttachmentData(const QVector& attachmentData); - virtual glm::vec3 getSkeletonPosition() const; - void updateLocalAABox(); DynamicCharacterController* getCharacterController() { return &_characterController; } - - void clearJointAnimationPriorities(); - glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } - float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } - QString getScriptedMotorFrame() const; - - void setScriptedMotorVelocity(const glm::vec3& velocity); - void setScriptedMotorTimescale(float timescale); - void setScriptedMotorFrame(QString frame); const QString& getCollisionSoundURL() {return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); void clearScriptableSettings(); - virtual void attach(const QString& modelURL, const QString& jointName = QString(), - const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, - bool allowDuplicates = false, bool useSaved = true); - /// Renders a laser pointer for UI picking - void renderLaserPointers(gpu::Batch& batch); + glm::vec3 getLaserPointerTipPosition(const PalmData* palm); - - const RecorderPointer getRecorder() const { return _recorder; } - const PlayerPointer getPlayer() const { return _player; } - + float getBoomLength() const { return _boomLength; } void setBoomLength(float boomLength) { _boomLength = boomLength; } - + static const float ZOOM_MIN; static const float ZOOM_MAX; static const float ZOOM_DEFAULT; - + public slots: void increaseSize(); void decreaseSize(); void resetSize(); - + void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); @@ -204,7 +166,7 @@ public slots: void clearReferential(); bool setModelReferential(const QUuid& id); bool setJointReferential(const QUuid& id, int jointIndex); - + bool isRecording(); qint64 recorderElapsed(); void startRecording(); @@ -213,7 +175,7 @@ public slots: void loadLastRecording(); virtual void rebuildSkeletonBody(); - + signals: void transformChanged(); void newCollisionSoundURL(const QUrl& url); @@ -221,6 +183,37 @@ signals: private: + QByteArray toByteArray(); + void simulate(float deltaTime); + void updateFromTrackers(float deltaTime); + virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; + virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override; + virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; + void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } + bool getShouldRenderLocally() const { return _shouldRender; } + bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; + bool isMyAvatar() const { return true; } + virtual int parseDataFromBuffer(const QByteArray& buffer); + virtual void setJointRotations(QVector jointRotations); + virtual void setJointData(int index, const glm::quat& rotation); + virtual void clearJointData(int index); + virtual void clearJointsData(); + virtual glm::vec3 getSkeletonPosition() const; + void updateLocalAABox(); + glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } + float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } + QString getScriptedMotorFrame() const; + void setScriptedMotorVelocity(const glm::vec3& velocity); + void setScriptedMotorTimescale(float timescale); + void setScriptedMotorFrame(QString frame); + virtual void attach(const QString& modelURL, const QString& jointName = QString(), + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, + bool allowDuplicates = false, bool useSaved = true); + + void renderLaserPointers(gpu::Batch& batch); + const RecorderPointer getRecorder() const { return _recorder; } + const PlayerPointer getPlayer() const { return _player; } + bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead @@ -235,7 +228,7 @@ private: bool _wasPushing; bool _isPushing; bool _isBraking; - + float _boomLength; float _trapDuration; // seconds that avatar has been trapped by collisions @@ -256,30 +249,30 @@ private: bool _shouldRender; bool _billboardValid; float _oculusYawOffset; - + bool _feetTouchFloor; eyeContactTarget _eyeContactTarget; RecorderPointer _recorder; - + glm::vec3 _trackedHeadPosition; - + Setting::Handle _realWorldFieldOfView; - - // private methods + + // private methods void updateOrientation(float deltaTime); glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool isHovering); glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity); void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); - + // Avatar Preferences bool _useFullAvatar = false; QUrl _fullAvatarURLFromPreferences; QUrl _headURLFromPreferences; QUrl _skeletonURLFromPreferences; - + QString _headModelName; QString _bodyModelName; QString _fullAvatarModelName; From 81375e47e07e02f9dd6798536e629d51242c53ce Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Jul 2015 09:07:26 -0700 Subject: [PATCH 030/109] remove methods from MyAvatar: setJointRotations setJointData clearJointData clearJointsData --- interface/src/avatar/MyAvatar.cpp | 27 --------------------------- interface/src/avatar/MyAvatar.h | 4 ---- 2 files changed, 31 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0dceb79402..fe4d4bc3cb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -900,33 +900,6 @@ glm::vec3 MyAvatar::getDefaultEyePosition() const { const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f; -void MyAvatar::setJointRotations(QVector jointRotations) { - int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size()); - for (int i = 0; i < numStates; ++i) { - // HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here - _skeletonModel.setJointState(i, true, jointRotations[i], RECORDER_PRIORITY); - } -} - -void MyAvatar::setJointData(int index, const glm::quat& rotation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY); - } -} - -void MyAvatar::clearJointData(int index) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _skeletonModel.setJointState(index, false, glm::quat(), 0.0f); - _skeletonModel.clearJointAnimationPriority(index); - } -} - -void MyAvatar::clearJointsData() { - clearJointAnimationPriorities(); -} - void MyAvatar::clearJointAnimationPriorities() { int numStates = _skeletonModel.getJointStateCount(); for (int i = 0; i < numStates; ++i) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4fd66c0347..f208b68520 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -194,10 +194,6 @@ private: bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; bool isMyAvatar() const { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer); - virtual void setJointRotations(QVector jointRotations); - virtual void setJointData(int index, const glm::quat& rotation); - virtual void clearJointData(int index); - virtual void clearJointsData(); virtual glm::vec3 getSkeletonPosition() const; void updateLocalAABox(); glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } From dac6667ee6dff76f2ba031de6c58d35e891bbaa8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 09:21:24 -0700 Subject: [PATCH 031/109] Start of state. --- libraries/animation/src/Rig.cpp | 69 +++++++++++++++++++++------- libraries/animation/src/Rig.h | 19 ++++++-- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7d520e7930..c0602dc870 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include "AnimationHandle.h" @@ -38,18 +39,15 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { void Rig::startAnimation(const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { - qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints; - AnimationHandlePointer handle = nullptr; + //qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints; foreach (const AnimationHandlePointer& candidate, _animationHandles) { if (candidate->getURL() == url) { - handle = candidate; - break; + candidate->start(); + return; } } - if (!handle) { - handle = createAnimationHandle(); - handle->setURL(url); - } + AnimationHandlePointer handle = createAnimationHandle(); + handle->setURL(url); handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); @@ -63,18 +61,17 @@ void Rig::startAnimation(const QString& url, float fps, float priority, void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) { // check for a configured animation for the role - qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; - AnimationHandlePointer handle = nullptr; + //qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; foreach (const AnimationHandlePointer& candidate, _animationHandles) { if (candidate->getRole() == role) { - handle = candidate; - break; + if (startAutomatically) { + candidate->start(); + } + return; } } - if (!handle) { - handle = createAnimationHandle(); - handle->setRole(role); - } + AnimationHandlePointer handle = createAnimationHandle(); + handle->setRole(role); handle->setURL(url); handle->setFPS(fps); handle->setPriority(priority); @@ -344,7 +341,45 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { return maybeCauterizeHead(jointIndex).getVisibleTransform(); } -void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform) { +void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation) { + glm::vec3 front = worldRotation * IDENTITY_FRONT; + glm::vec3 delta = worldPosition - _lastPosition ; + float forwardSpeed = glm::dot(delta, front) / deltaTime; + float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime; + bool isWalking = std::abs(forwardSpeed) > 0.01; + bool isTurning = std::abs(rotationalSpeed) > 0.5; + + // Crude, until we have blending: + const float EXPECTED_INTERVAL = 1.0f / 60.0f; + if (deltaTime >= EXPECTED_INTERVAL) { + isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins. + isTurning = false; // FIXME + bool isIdle = !isWalking && !isTurning; + auto singleRole = [](bool walking, bool turning, bool idling) { + return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : "")); + }; + QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle); + if (!toStop.isEmpty()) { + //qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed; + //stopAnimationByRole(toStop); + } + QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle); + if (!newRole.isEmpty()) { + //startAnimationByRole(newRole); + qCDebug(animation) << deltaTime << ":" /*<< _lastPosition << worldPosition << "=>" */<< delta << "." << front << "=> " << forwardSpeed << newRole; + /*if (newRole == "idle") { + qCDebug(animation) << deltaTime << ":" << _lastPosition << worldPosition << "=>" << delta; + }*/ + } + + _lastPosition = worldPosition; + _positions[(++_positionIndex) % _positions.count()] = worldPosition; // exp. alt. to above line + _lastFront = front; + _isWalking = isWalking; + _isTurning = isTurning; + _isIdle = isIdle; + } + // update animations foreach (const AnimationHandlePointer& handle, _runningAnimations) { handle->simulate(deltaTime); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2087ad6800..b56151b5be 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -71,8 +71,9 @@ public: float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); void stopAnimationByRole(const QString& role); - void addAnimationByRole(const QString& role, const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically); + void addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, + float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, + float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false); float initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; @@ -107,7 +108,7 @@ public: void setJointTransform(int jointIndex, glm::mat4 newTransform); glm::mat4 getJointVisibleTransform(int jointIndex) const; void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); - void simulateInternal(float deltaTime, glm::mat4 parentTransform); + void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation); bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, const QVector& freeLineage, glm::mat4 parentTransform); @@ -144,6 +145,16 @@ public: std::vector _headBones; bool _jointsAreDirty = false; int _neckJointIndex = -1; -}; + + bool _isWalking; + bool _isTurning; + bool _isIdle; + glm::vec3 _lastFront; + glm::vec3 _lastPosition; + // or, experimentally... + QVector _positions = QVector(4); + QVector _timeIntervals = QVector(4); + int _positionIndex; + }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ddf18f6ef8..a316da0f99 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1346,7 +1346,7 @@ void Model::simulateInternal(float deltaTime) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->simulateInternal(deltaTime, parentTransform); + _rig->simulateInternal(deltaTime, parentTransform, getTranslation(), getRotation()); _shapesAreDirty = !_shapes.isEmpty(); From 73ac941230434fd4a14503f80b5c93fc0328b0da Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jul 2015 11:15:33 -0700 Subject: [PATCH 032/109] remove unused legacy Shapes from Model and friends --- interface/src/avatar/Avatar.cpp | 37 +---- interface/src/avatar/Avatar.h | 20 --- interface/src/avatar/MyAvatar.cpp | 2 - interface/src/avatar/SkeletonModel.cpp | 8 +- libraries/render-utils/src/Model.cpp | 24 ---- libraries/render-utils/src/Model.h | 6 - libraries/render-utils/src/PhysicsEntity.cpp | 139 +------------------ libraries/render-utils/src/PhysicsEntity.h | 22 --- 8 files changed, 8 insertions(+), 250 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 92fe39687d..ea9e80c132 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -431,15 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } } + /* + // TODO: re-implement these when we have more detailed avatar collision shapes bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes); - bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); - bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderSkeleton) { - _skeletonModel.renderJointCollisionShapes(0.7f); } + bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); if (renderHead && shouldRenderHead(renderArgs)) { - getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } + */ + + bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderBounding && shouldRenderHead(renderArgs)) { _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } @@ -794,33 +796,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); } -bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { - bool hit = _skeletonModel.findRayIntersection(intersection); - hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit; - return hit; -} - -bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) { - return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions); - // TODO: Andrew to fix: Temporarily disabling collisions against the head - //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); -} - -bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - return _skeletonModel.findPlaneCollisions(plane, collisions) || - getHead()->getFaceModel().findPlaneCollisions(plane, collisions); -} - -bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { - // TODO: Andrew to fix: also collide against _skeleton - //bool collided = _skeletonModel.findCollisions(shapes, collisions); - - Model& headModel = getHead()->getFaceModel(); - //collided = headModel.findCollisions(shapes, collisions) || collided; - bool collided = headModel.findCollisions(shapes, collisions); - return collided; -} - void Avatar::setSkeletonOffset(const glm::vec3& offset) { const float MAX_OFFSET_LENGTH = _scale * 0.5f; float offsetLength = glm::length(offset); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index a587e69642..dcf37c9a1e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -110,26 +110,6 @@ public: /// Returns the distance to use as a LOD parameter. float getLODDistance() const; - bool findRayIntersection(RayIntersectionInfo& intersection) const; - - /// \param shapes list of shapes to collide against avatar - /// \param collisions list to store collision results - /// \return true if at least one shape collided with avatar - bool findCollisions(const QVector& shapes, CollisionList& collisions); - - /// Checks for penetration between the a sphere and the avatar's models. - /// \param penetratorCenter the center of the penetration test sphere - /// \param penetratorRadius the radius of the penetration test sphere - /// \param collisions[out] a list to which collisions get appended - /// \return whether or not the sphere penetrated - bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions); - - /// Checks for penetration between the described plane and the avatar. - /// \param plane the penetration plane - /// \param collisions[out] a list to which collisions get appended - /// \return whether or not the plane penetrated - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - virtual bool isMyAvatar() const { return false; } virtual QVector getJointRotations() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0dceb79402..66c59eb35d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -110,8 +110,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _driveKeys[i] = 0.0f; } - _skeletonModel.setEnableShapes(true); - // connect to AddressManager signal for location jumps connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 08960c913c..19dc2397c5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -12,9 +12,7 @@ #include #include -#include #include -#include #include "Application.h" #include "Avatar.h" @@ -44,7 +42,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r { assert(_rig); assert(_owningAvatar); - _enableShapes = true; } SkeletonModel::~SkeletonModel() { @@ -81,10 +78,7 @@ void SkeletonModel::initJointStates(QVector states) { _rig->updateJointState(i, parentTransform); } - clearShapes(); - if (_enableShapes) { - buildShapes(); - } + buildShapes(); Extents meshExtents = getMeshExtents(); _headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a316da0f99..c28dbf4247 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -23,8 +23,6 @@ #include #include #include "PhysicsEntity.h" -#include -#include #include #include "AbstractViewStateInterface.h" @@ -223,10 +221,6 @@ void Model::setScaleInternal(const glm::vec3& scale) { if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; initJointTransforms(); - if (_shapes.size() > 0) { - clearShapes(); - buildShapes(); - } } } @@ -1169,15 +1163,6 @@ QStringList Model::getJointNames() const { return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); } -// virtual override from PhysicsEntity -void Model::buildShapes() { - // TODO: figure out how to load/build collision shapes for general models -} - -void Model::updateShapePositions() { - // TODO: implement this when we know how to build shapes for regular Models -} - class Blender : public QRunnable { public: @@ -1348,8 +1333,6 @@ void Model::simulateInternal(float deltaTime) { glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; _rig->simulateInternal(deltaTime, parentTransform, getTranslation(), getRotation()); - _shapesAreDirty = !_shapes.isEmpty(); - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1389,7 +1372,6 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; if (_rig->setJointPosition(jointIndex, position, rotation, useRotation, lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) { - _shapesAreDirty = !_shapes.isEmpty(); return true; } return false; @@ -1400,7 +1382,6 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm: const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); - _shapesAreDirty = !_shapes.isEmpty(); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { @@ -1415,10 +1396,6 @@ float Model::getLimbLength(int jointIndex) const { return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } -void Model::renderJointCollisionShapes(float alpha) { - // implement this when we have shapes for regular models -} - bool Model::maybeStartBlender() { const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); if (fbxGeometry.hasBlendedMeshes()) { @@ -1484,7 +1461,6 @@ void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _rig->clearJointStates(); _meshStates.clear(); - clearShapes(); _rig->deleteAnimations(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 45d7ce63ab..66f3c63f29 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -39,7 +39,6 @@ class AbstractViewStateInterface; class QScriptEngine; -class Shape; #include "RenderArgs.h" class ViewFrustum; @@ -90,7 +89,6 @@ public: void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } - virtual void renderJointCollisionShapes(float alpha); bool isVisible() const { return _isVisible; } @@ -234,10 +232,6 @@ protected: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; - // virtual overrides from PhysicsEntity - virtual void buildShapes(); - virtual void updateShapePositions(); - void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } QSharedPointer _geometry; diff --git a/libraries/render-utils/src/PhysicsEntity.cpp b/libraries/render-utils/src/PhysicsEntity.cpp index 155b2fe4e0..5d58d87e84 100644 --- a/libraries/render-utils/src/PhysicsEntity.cpp +++ b/libraries/render-utils/src/PhysicsEntity.cpp @@ -11,17 +11,10 @@ #include "PhysicsEntity.h" -#include "PlaneShape.h" -#include "Shape.h" -#include "ShapeCollider.h" -#include "SphereShape.h" - PhysicsEntity::PhysicsEntity() : _translation(0.0f), _rotation(), - _boundingRadius(0.0f), - _shapesAreDirty(true), - _enableShapes(false) { + _boundingRadius(0.0f) { } PhysicsEntity::~PhysicsEntity() { @@ -29,143 +22,13 @@ PhysicsEntity::~PhysicsEntity() { void PhysicsEntity::setTranslation(const glm::vec3& translation) { if (_translation != translation) { - _shapesAreDirty = !_shapes.isEmpty(); _translation = translation; } } void PhysicsEntity::setRotation(const glm::quat& rotation) { if (_rotation != rotation) { - _shapesAreDirty = !_shapes.isEmpty(); _rotation = rotation; } } -void PhysicsEntity::setShapeBackPointers() { - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setEntity(this); - } - } -} - -void PhysicsEntity::setEnableShapes(bool enable) { - if (enable != _enableShapes) { - clearShapes(); - _enableShapes = enable; - if (_enableShapes) { - buildShapes(); - } - } -} - -void PhysicsEntity::clearShapes() { - for (int i = 0; i < _shapes.size(); ++i) { - delete _shapes[i]; - } - _shapes.clear(); -} - -bool PhysicsEntity::findRayIntersection(RayIntersectionInfo& intersection) const { - return ShapeCollider::findRayIntersection(_shapes, intersection); -} - -bool PhysicsEntity::findCollisions(const QVector shapes, CollisionList& collisions) { - bool collided = false; - int numTheirShapes = shapes.size(); - for (int i = 0; i < numTheirShapes; ++i) { - const Shape* theirShape = shapes[i]; - if (!theirShape) { - continue; - } - int numOurShapes = _shapes.size(); - for (int j = 0; j < numOurShapes; ++j) { - const Shape* ourShape = _shapes.at(j); - if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { - collided = true; - } - } - } - return collided; -} - -bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions) { - bool collided = false; - SphereShape sphere(sphereRadius, sphereCenter); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; -} - -bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - bool collided = false; - PlaneShape planeShape(plane); - for (int i = 0; i < _shapes.size(); i++) { - if (_shapes.at(i) && ShapeCollider::collideShapes(&planeShape, _shapes.at(i), collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; -} - -// ----------------------------------------------------------- -// TODO: enforce this maximum when shapes are actually built. The gotcha here is -// that the Model class (derived from PhysicsEntity) expects numShapes == numJoints, -// so we have to modify that code to be safe. -const int MAX_SHAPES_PER_ENTITY = 256; - -// the first 256 prime numbers -const int primes[256] = { - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, - 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, - 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, - 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, - 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, - 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, - 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, - 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, - 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, - 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, - 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, - 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, - 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, - 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, - 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, - 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, - 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, - 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, - 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, - 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, - 1597, 1601, 1607, 1609, 1613, 1619 }; - -void PhysicsEntity::disableCollisions(int shapeIndexA, int shapeIndexB) { - if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { - _disabledCollisions.insert(primes[shapeIndexA] * primes[shapeIndexB]); - } -} - -bool PhysicsEntity::collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const { - if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { - return !_disabledCollisions.contains(primes[shapeIndexA] * primes[shapeIndexB]); - } - return false; -} diff --git a/libraries/render-utils/src/PhysicsEntity.h b/libraries/render-utils/src/PhysicsEntity.h index f01f1d10a6..3b527c7827 100644 --- a/libraries/render-utils/src/PhysicsEntity.h +++ b/libraries/render-utils/src/PhysicsEntity.h @@ -21,8 +21,6 @@ #include #include -class Shape; - class PhysicsEntity { public: @@ -38,30 +36,10 @@ public: const glm::quat& getRotation() const { return _rotation; } float getBoundingRadius() const { return _boundingRadius; } - void setShapeBackPointers(); - - void setEnableShapes(bool enable); - - virtual void buildShapes() = 0; - virtual void clearShapes(); - const QVector getShapes() const { return _shapes; } - - bool findRayIntersection(RayIntersectionInfo& intersection) const; - bool findCollisions(const QVector shapes, CollisionList& collisions); - bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions); - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - - void disableCollisions(int shapeIndexA, int shapeIndexB); - bool collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const; - protected: glm::vec3 _translation; glm::quat _rotation; float _boundingRadius; - bool _shapesAreDirty; - bool _enableShapes; - QVector _shapes; - QSet _disabledCollisions; }; #endif // hifi_PhysicsEntity_h From 81831975b526f8a2b62a3c18e008d67f94be0c87 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jul 2015 12:27:17 -0700 Subject: [PATCH 033/109] remove legacy CapsuleShape from SkeletonModel --- interface/src/avatar/Avatar.cpp | 5 ++-- interface/src/avatar/MyAvatar.cpp | 7 ++--- interface/src/avatar/SkeletonModel.cpp | 39 +++++++++++--------------- interface/src/avatar/SkeletonModel.h | 12 ++++---- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ea9e80c132..e29e5e4408 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1115,9 +1115,8 @@ void Avatar::setShowDisplayName(bool showDisplayName) { // virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { - const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); - shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight()); - shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset()); + shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight()); + shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset()); } // virtual diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 66c59eb35d..cdd7bf2b66 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1088,11 +1088,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { void MyAvatar::rebuildSkeletonBody() { // compute localAABox - const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); - float radius = capsule.getRadius(); - float height = 2.0f * (capsule.getHalfHeight() + radius); + float radius = _skeletonModel.getBoundingCapsuleRadius(); + float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius; glm::vec3 corner(-radius, -0.5f * height, -radius); - corner += _skeletonModel.getBoundingShapeOffset(); + corner += _skeletonModel.getBoundingCapsuleOffset(); glm::vec3 scale(2.0f * radius, height, 2.0f * radius); _characterController.setLocalBoundingBox(corner, scale); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 19dc2397c5..5c9cdd95a3 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -32,8 +32,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r Model(rig, parent), _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), - _boundingShape(), - _boundingShapeLocalOffset(0.0f), + _boundingCapsuleLocalOffset(0.0f), + _boundingCapsuleRadius(0.0f), + _boundingCapsuleHeight(0.0f), _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), _standingFoot(NO_FOOT), _standingOffset(0.0f), @@ -177,9 +178,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { if (_rig->getJointsAreDirty()) { updateClusterMatrices(); } - - _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); } void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { @@ -671,12 +669,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // NOTE: we assume that the longest side of totalExtents is the yAxis... glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; // ... and assume the radius is half the RMS of the X and Z sides: - float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _boundingShape.setRadius(capsuleRadius); - _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); + _boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + _boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius; glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; + _boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); } @@ -687,30 +684,26 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha auto deferredLighting = DependencyManager::get(); Transform transform; // = Transform(); - // draw a blue sphere at the capsule end point - glm::vec3 endPoint; - _boundingShape.getEndPoint(endPoint); - endPoint = endPoint + _translation; - transform.setTranslation(endPoint); + // draw a blue sphere at the capsule top point + glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f); + transform.setTranslation(topPoint); batch.setModelTransform(transform); deferredLighting->bindSimpleProgram(batch); - geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, + geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); - // draw a yellow sphere at the capsule start point - glm::vec3 startPoint; - _boundingShape.getStartPoint(startPoint); - startPoint = startPoint + _translation; - glm::vec3 axis = endPoint - startPoint; - transform.setTranslation(startPoint); + // draw a yellow sphere at the capsule bottom point + glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f); + glm::vec3 axis = topPoint - bottomPoint; + transform.setTranslation(bottomPoint); batch.setModelTransform(transform); deferredLighting->bindSimpleProgram(batch); - geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, + geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.8f, 0.8f, 0.6f, alpha)); // draw a green cylinder between the two points glm::vec3 origin(0.0f); - Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(), + Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius, glm::vec4(0.6f, 0.8f, 0.6f, alpha)); } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 14dc2da3f0..89c7ab1fcb 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -13,7 +13,6 @@ #define hifi_SkeletonModel_h -#include #include class Avatar; @@ -98,9 +97,9 @@ public: void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha); - float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } - const CapsuleShape& getBoundingShape() const { return _boundingShape; } - const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; } + float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; } + float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; } + const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; } bool hasSkeleton(); @@ -157,8 +156,9 @@ private: Avatar* _owningAvatar; - CapsuleShape _boundingShape; - glm::vec3 _boundingShapeLocalOffset; + glm::vec3 _boundingCapsuleLocalOffset; + float _boundingCapsuleRadius; + float _boundingCapsuleHeight; glm::vec3 _defaultEyeModelPosition; int _standingFoot; From 990f0d6d0712451396467e17520aa41e6ae9cd85 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 12:57:39 -0700 Subject: [PATCH 034/109] Pass correct position/velocity/orientation to Rig simulation. --- libraries/animation/src/Rig.cpp | 28 ++++++++++++---------------- libraries/animation/src/Rig.h | 4 +++- libraries/render-utils/src/Model.cpp | 8 ++++---- libraries/render-utils/src/Model.h | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c0602dc870..9b2cd10d9d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -341,17 +341,16 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { return maybeCauterizeHead(jointIndex).getVisibleTransform(); } -void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation) { - glm::vec3 front = worldRotation * IDENTITY_FRONT; - glm::vec3 delta = worldPosition - _lastPosition ; - float forwardSpeed = glm::dot(delta, front) / deltaTime; - float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime; - bool isWalking = std::abs(forwardSpeed) > 0.01; - bool isTurning = std::abs(rotationalSpeed) > 0.5; +void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { - // Crude, until we have blending: - const float EXPECTED_INTERVAL = 1.0f / 60.0f; - if (deltaTime >= EXPECTED_INTERVAL) { + if (_enableRig) { + glm::vec3 front = worldRotation * IDENTITY_FRONT; + float forwardSpeed = glm::dot(worldVelocity, front); + float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime; + bool isWalking = std::abs(forwardSpeed) > 0.01; + bool isTurning = std::abs(rotationalSpeed) > 0.5; + + // Crude, until we have blending: isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins. isTurning = false; // FIXME bool isIdle = !isWalking && !isTurning; @@ -361,15 +360,12 @@ void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle); if (!toStop.isEmpty()) { //qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed; - //stopAnimationByRole(toStop); + stopAnimationByRole(toStop); } QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle); if (!newRole.isEmpty()) { - //startAnimationByRole(newRole); - qCDebug(animation) << deltaTime << ":" /*<< _lastPosition << worldPosition << "=>" */<< delta << "." << front << "=> " << forwardSpeed << newRole; - /*if (newRole == "idle") { - qCDebug(animation) << deltaTime << ":" << _lastPosition << worldPosition << "=>" << delta; - }*/ + startAnimationByRole(newRole); + qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole; } _lastPosition = worldPosition; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index b56151b5be..45108936d9 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -108,7 +108,7 @@ public: void setJointTransform(int jointIndex, glm::mat4 newTransform); glm::mat4 getJointVisibleTransform(int jointIndex) const; void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); - void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation); + void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, const QVector& freeLineage, glm::mat4 parentTransform); @@ -132,6 +132,7 @@ public: virtual bool getIsFirstPerson() const { return _isFirstPerson; } bool getJointsAreDirty() { return _jointsAreDirty; } + bool setEnableRig(bool isEnabled) { _enableRig = isEnabled; } protected: QVector _jointStates; @@ -146,6 +147,7 @@ public: bool _jointsAreDirty = false; int _neckJointIndex = -1; + bool _enableRig; bool _isWalking; bool _isTurning; bool _isIdle; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a316da0f99..cfe25c1b8c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1316,7 +1316,7 @@ void Model::snapToRegistrationPoint() { _snappedToRegistrationPoint = true; } -void Model::simulate(float deltaTime, bool fullUpdate) { +void Model::simulate(float deltaTime, bool fullUpdate, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); @@ -1337,16 +1337,16 @@ void Model::simulate(float deltaTime, bool fullUpdate) { if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) { snapToRegistrationPoint(); } - simulateInternal(deltaTime); + simulateInternal(deltaTime, worldPosition, worldVelocity, worldRotation); } } -void Model::simulateInternal(float deltaTime) { +void Model::simulateInternal(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { // update the world space transforms for all joints const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->simulateInternal(deltaTime, parentTransform, getTranslation(), getRotation()); + _rig->simulateInternal(deltaTime, parentTransform, worldPosition, worldVelocity, worldRotation); _shapesAreDirty = !_shapes.isEmpty(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 45d7ce63ab..88446aba47 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -117,7 +117,7 @@ public: void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } - virtual void simulate(float deltaTime, bool fullUpdate = true); + virtual void simulate(float deltaTime, bool fullUpdate = true, const glm::vec3& worldPosition = glm::vec3(), const glm::vec3& worldVelocity = glm::vec3(), const glm::quat& worldRotation = glm::quat()); /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -274,7 +274,7 @@ protected: void scaleToFit(); void snapToRegistrationPoint(); - void simulateInternal(float deltaTime); + void simulateInternal(float deltaTime, const glm::vec3& worldPosition = glm::vec3(), const glm::vec3& worldVelocity = glm::vec3(), const glm::quat& worldRotation = glm::quat()); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); From 8196770ed31217d761ee8f58e104b5f23d28dc6e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 12:58:43 -0700 Subject: [PATCH 035/109] Wiring, including avatar position/velocity/orientation data, and an enableRig setting so that we don't break stuff unless turned on. --- interface/src/avatar/Head.cpp~ | 416 +++++++++++++++++++++++++ interface/src/avatar/MyAvatar.cpp | 1 + interface/src/avatar/SkeletonModel.cpp | 2 +- tests/rig/src/RigTests.cpp | 3 +- 4 files changed, 420 insertions(+), 2 deletions(-) create mode 100644 interface/src/avatar/Head.cpp~ diff --git a/interface/src/avatar/Head.cpp~ b/interface/src/avatar/Head.cpp~ new file mode 100644 index 0000000000..0dace70b3c --- /dev/null +++ b/interface/src/avatar/Head.cpp~ @@ -0,0 +1,416 @@ +// +// Head.cpp +// interface/src/avatar +// +// Copyright 2013 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 +#include + +#include +#include +#include + +#include "Application.h" +#include "Avatar.h" +#include "GeometryUtil.h" +#include "Head.h" +#include "Menu.h" +#include "Util.h" +#include "devices/DdeFaceTracker.h" +#include "devices/Faceshift.h" +#include "AvatarRig.h" + +using namespace std; + +Head::Head(Avatar* owningAvatar) : + HeadData((AvatarData*)owningAvatar), + _returnHeadToCenter(false), + _position(0.0f, 0.0f, 0.0f), + _rotation(0.0f, 0.0f, 0.0f), + _leftEyePosition(0.0f, 0.0f, 0.0f), + _rightEyePosition(0.0f, 0.0f, 0.0f), + _eyePosition(0.0f, 0.0f, 0.0f), + _scale(1.0f), + _lastLoudness(0.0f), + _longTermAverageLoudness(-1.0f), + _audioAttack(0.0f), + _audioJawOpen(0.0f), + _mouth2(0.0f), + _mouth3(0.0f), + _mouth4(0.0f), + _renderLookatVectors(false), + _saccade(0.0f, 0.0f, 0.0f), + _saccadeTarget(0.0f, 0.0f, 0.0f), + _leftEyeBlinkVelocity(0.0f), + _rightEyeBlinkVelocity(0.0f), + _timeWithoutTalking(0.0f), + _deltaPitch(0.0f), + _deltaYaw(0.0f), + _deltaRoll(0.0f), + _deltaLeanSideways(0.0f), + _deltaLeanForward(0.0f), + _isCameraMoving(false), + _isLookingAtMe(false), +<<<<<<< HEAD + _faceModel(this, std::make_shared()), +======= + _lookingAtMeStarted(0), + _wasLastLookingAtMe(0), + _faceModel(this), +>>>>>>> 8a34df380cac67142dbb30bc20e8e022fdd551e0 + _leftEyeLookAtID(DependencyManager::get()->allocateID()), + _rightEyeLookAtID(DependencyManager::get()->allocateID()) +{ +} + +void Head::init() { + _faceModel.init(); +} + +void Head::reset() { + _baseYaw = _basePitch = _baseRoll = 0.0f; + _leanForward = _leanSideways = 0.0f; + _faceModel.reset(); +} + +void Head::simulate(float deltaTime, bool isMine, bool billboard) { + // Update audio trailing average for rendering facial animations + const float AUDIO_AVERAGING_SECS = 0.05f; + const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; + _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); + + if (_longTermAverageLoudness == -1.0f) { + _longTermAverageLoudness = _averageLoudness; + } else { + _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); + } + + if (isMine) { + MyAvatar* myAvatar = static_cast(_owningAvatar); + + // Only use face trackers when not playing back a recording. + if (!myAvatar->isPlaying()) { + FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker(); + _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); + if (_isFaceTrackerConnected) { + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + + if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { + + if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { + calculateMouthShapes(); + + const int JAW_OPEN_BLENDSHAPE = 21; + const int MMMM_BLENDSHAPE = 34; + const int FUNNEL_BLENDSHAPE = 40; + const int SMILE_LEFT_BLENDSHAPE = 28; + const int SMILE_RIGHT_BLENDSHAPE = 29; + _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; + _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; + _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; + } + + applyEyelidOffset(getFinalOrientationInWorldFrame()); + } + } + } + // Twist the upper body to follow the rotation of the head, but only do this with my avatar, + // since everyone else will see the full joint rotations for other people. + const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f; + const float BODY_FOLLOW_HEAD_FACTOR = 0.66f; + float currentTwist = getTorsoTwist(); + setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE); + } + + if (!(_isFaceTrackerConnected || billboard)) { + // Update eye saccades + const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; + const float AVERAGE_SACCADE_INTERVAL = 6.0f; + const float MICROSACCADE_MAGNITUDE = 0.002f; + const float SACCADE_MAGNITUDE = 0.04f; + const float NOMINAL_FRAME_RATE = 60.0f; + + if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { + _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); + } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { + _saccadeTarget = SACCADE_MAGNITUDE * randVector(); + } + _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); + + // Detect transition from talking to not; force blink after that and a delay + bool forceBlink = false; + const float TALKING_LOUDNESS = 100.0f; + const float BLINK_AFTER_TALKING = 0.25f; + if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { + _timeWithoutTalking = 0.0f; + + } else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) { + forceBlink = true; + } + + // Update audio attack data for facial animation (eyebrows and mouth) + const float AUDIO_ATTACK_AVERAGING_RATE = 0.9f; + _audioAttack = AUDIO_ATTACK_AVERAGING_RATE * _audioAttack + (1.0f - AUDIO_ATTACK_AVERAGING_RATE) * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness); + _lastLoudness = (_audioLoudness - _longTermAverageLoudness); + + const float BROW_LIFT_THRESHOLD = 100.0f; + if (_audioAttack > BROW_LIFT_THRESHOLD) { + _browAudioLift += sqrtf(_audioAttack) * 0.01f; + } + _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); + + const float BLINK_SPEED = 10.0f; + const float BLINK_SPEED_VARIABILITY = 1.0f; + const float BLINK_START_VARIABILITY = 0.25f; + const float FULLY_OPEN = 0.0f; + const float FULLY_CLOSED = 1.0f; + if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { + // no blinking when brows are raised; blink less with increasing loudness + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * + ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { + _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + if (randFloat() < 0.5f) { + _leftEyeBlink = BLINK_START_VARIABILITY; + } else { + _rightEyeBlink = BLINK_START_VARIABILITY; + } + } + } else { + _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + + if (_leftEyeBlink == FULLY_CLOSED) { + _leftEyeBlinkVelocity = -BLINK_SPEED; + + } else if (_leftEyeBlink == FULLY_OPEN) { + _leftEyeBlinkVelocity = 0.0f; + } + if (_rightEyeBlink == FULLY_CLOSED) { + _rightEyeBlinkVelocity = -BLINK_SPEED; + + } else if (_rightEyeBlink == FULLY_OPEN) { + _rightEyeBlinkVelocity = 0.0f; + } + } + + // use data to update fake Faceshift blendshape coefficients + calculateMouthShapes(); + DependencyManager::get()->updateFakeCoefficients(_leftEyeBlink, + _rightEyeBlink, + _browAudioLift, + _audioJawOpen, + _mouth2, + _mouth3, + _mouth4, + _blendshapeCoefficients); + + applyEyelidOffset(getOrientation()); + + } else { + _saccade = glm::vec3(); + } + + if (!isMine) { + _faceModel.setLODDistance(static_cast(_owningAvatar)->getLODDistance()); + } + _leftEyePosition = _rightEyePosition = getPosition(); + if (!billboard) { + _faceModel.simulate(deltaTime); + if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) { + static_cast(_owningAvatar)->getSkeletonModel().getEyePositions(_leftEyePosition, _rightEyePosition); + } + } + _eyePosition = calculateAverageEyePosition(); +} + +void Head::calculateMouthShapes() { + const float JAW_OPEN_SCALE = 0.015f; + const float JAW_OPEN_RATE = 0.9f; + const float JAW_CLOSE_RATE = 0.90f; + float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE; + if (audioDelta > _audioJawOpen) { + _audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE; + } else { + _audioJawOpen *= JAW_CLOSE_RATE; + } + _audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f); + + // _mouth2 = "mmmm" shape + // _mouth3 = "funnel" shape + // _mouth4 = "smile" shape + const float FUNNEL_PERIOD = 0.985f; + const float FUNNEL_RANDOM_PERIOD = 0.01f; + const float MMMM_POWER = 0.25f; + const float MMMM_PERIOD = 0.91f; + const float MMMM_RANDOM_PERIOD = 0.15f; + const float SMILE_PERIOD = 0.925f; + const float SMILE_RANDOM_PERIOD = 0.05f; + + _mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD); + _mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD); + _mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD); +} + +void Head::applyEyelidOffset(glm::quat headOrientation) { + // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. + + glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getCorrectedLookAtPosition() - _eyePosition); + eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head + float eyePitch = safeEulerAngles(eyeRotation).x; + + const float EYE_PITCH_TO_COEFFICIENT = 1.6f; // Empirically determined + const float MAX_EYELID_OFFSET = 0.8f; // So that don't fully close eyes when looking way down + float eyelidOffset = glm::clamp(-eyePitch * EYE_PITCH_TO_COEFFICIENT, -1.0f, MAX_EYELID_OFFSET); + + for (int i = 0; i < 2; i++) { + const int LEFT_EYE = 8; + float eyeCoefficient = _blendshapeCoefficients[i] - _blendshapeCoefficients[LEFT_EYE + i]; // Raw value + eyeCoefficient = glm::clamp(eyelidOffset + eyeCoefficient * (1.0f - eyelidOffset), -1.0f, 1.0f); + if (eyeCoefficient > 0.0f) { + _blendshapeCoefficients[i] = eyeCoefficient; + _blendshapeCoefficients[LEFT_EYE + i] = 0.0f; + + } else { + _blendshapeCoefficients[i] = 0.0f; + _blendshapeCoefficients[LEFT_EYE + i] = -eyeCoefficient; + } + } +} + +void Head::relaxLean(float deltaTime) { + // restore rotation, lean to neutral positions + const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds + float relaxationFactor = 1.0f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.0f); + _deltaYaw *= relaxationFactor; + _deltaPitch *= relaxationFactor; + _deltaRoll *= relaxationFactor; + _leanSideways *= relaxationFactor; + _leanForward *= relaxationFactor; + _deltaLeanSideways *= relaxationFactor; + _deltaLeanForward *= relaxationFactor; +} + +void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) { + if (_renderLookatVectors) { + renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); + } +} + +void Head::setScale (float scale) { + if (_scale == scale) { + return; + } + _scale = scale; +} + +glm::quat Head::getFinalOrientationInWorldFrame() const { + return _owningAvatar->getOrientation() * getFinalOrientationInLocalFrame(); +} + +glm::quat Head::getFinalOrientationInLocalFrame() const { + return glm::quat(glm::radians(glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() ))); +} + +glm::vec3 Head::getCorrectedLookAtPosition() { + if (isLookingAtMe()) { + return _correctedLookAtPosition; + } else { + return getLookAtPosition(); + } +} + +void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { + 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 + // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not + // always the same. + if (qApp->isHMDMode()) { + return getOrientation(); + } + Avatar* owningAvatar = static_cast(_owningAvatar); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); +} + +glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { + glm::quat orientation = getOrientation(); + glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; + return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; +} + +glm::vec3 Head::getScalePivot() const { + return _faceModel.isActive() ? _faceModel.getTranslation() : _position; +} + +void Head::setFinalPitch(float finalPitch) { + _deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch; +} + +void Head::setFinalYaw(float finalYaw) { + _deltaYaw = glm::clamp(finalYaw, MIN_HEAD_YAW, MAX_HEAD_YAW) - _baseYaw; +} + +void Head::setFinalRoll(float finalRoll) { + _deltaRoll = glm::clamp(finalRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL) - _baseRoll; +} + +float Head::getFinalYaw() const { + return glm::clamp(_baseYaw + _deltaYaw, MIN_HEAD_YAW, MAX_HEAD_YAW); +} + +float Head::getFinalPitch() const { + return glm::clamp(_basePitch + _deltaPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); +} + +float Head::getFinalRoll() const { + return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); +} + +void Head::addLeanDeltas(float sideways, float forward) { + _deltaLeanSideways += sideways; + _deltaLeanForward += forward; +} + +void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { + auto& batch = *renderArgs->_batch; + auto transform = Transform{}; + batch.setModelTransform(transform); + batch._glLineWidth(2.0f); + + auto deferredLighting = DependencyManager::get(); + deferredLighting->bindSimpleProgram(batch); + + auto geometryCache = DependencyManager::get(); + glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f); + glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f); + geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); + geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); +} + + diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0dceb79402..f1afd235bf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -742,6 +742,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); settings.endGroup(); + _rig->setEnableRig(settings.value("enableRig").toBool()); } void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 08960c913c..dfceab24aa 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -127,7 +127,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients()); - Model::simulate(deltaTime, fullUpdate); + Model::simulate(deltaTime, fullUpdate, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index 74047c0162..05cef428c6 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -69,7 +69,7 @@ void RigTests::initTestCase() { loop.exec();*/ // Blocking all further tests until signalled. // Joint geometry from fbx. -#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" +//#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" #ifdef FROM_FILE QFile file(FROM_FILE); QCOMPARE(file.open(QIODevice::ReadOnly), true); @@ -124,4 +124,5 @@ void reportSome(RigPointer rig) { void RigTests::initialPoseArmsDown() { //reportAll(_rig); reportSome(_rig); + QCOMPARE(false, true); } From 146ddd224028dce6bd4d99f00acc9c7d3deb1bae Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 13:42:18 -0700 Subject: [PATCH 036/109] Cleanup false starts. --- libraries/animation/src/Rig.cpp | 1 - libraries/animation/src/Rig.h | 4 ---- libraries/avatars/src/AvatarData.cpp | 1 - libraries/avatars/src/AvatarData.h | 3 --- tests/rig/src/RigTests.cpp | 29 +++++----------------------- 5 files changed, 5 insertions(+), 33 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9b2cd10d9d..d20abc58d6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -369,7 +369,6 @@ void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm } _lastPosition = worldPosition; - _positions[(++_positionIndex) % _positions.count()] = worldPosition; // exp. alt. to above line _lastFront = front; _isWalking = isWalking; _isTurning = isTurning; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 45108936d9..379ed64877 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -153,10 +153,6 @@ public: bool _isIdle; glm::vec3 _lastFront; glm::vec3 _lastPosition; - // or, experimentally... - QVector _positions = QVector(4); - QVector _timeIntervals = QVector(4); - int _positionIndex; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5a0ba6c674..5b970a95a3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1087,7 +1087,6 @@ void AvatarData::setJointMappingsFromNetworkReply() { } networkReply->deleteLater(); - emit jointMappingLoaded(); } void AvatarData::sendAvatarDataPacket() { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 60c643eff9..a020be0f7a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -312,9 +312,6 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } -signals: - void jointMappingLoaded(); // So that test cases or anyone waiting on asynchronous loading can be informed. - public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index 05cef428c6..f1eb7cc440 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -52,23 +52,6 @@ QTEST_MAIN(RigTests) void RigTests::initTestCase() { - - // There are two good ways we could organize this: - // 1. Create a MyAvatar the same way that Interface does, and poke at it. - // We can't do that because MyAvatar (and even Avatar) are in interface, not a library, and our build system won't allow that dependency. - // 2. Create just the minimum skeleton in the most direct way possible, using only very basic library APIs (such as fbx). - // I don't think we can do that because not everything we need is exposed directly from, e.g., the fst and fbx readers. - // So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and - // duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path. - - // Joint mapping from fst. FIXME: Do we need this??? - /*auto avatar = std::make_shared(); - QEventLoop loop; // Create an event loop that will quit when we get the finished signal - QObject::connect(avatar.get(), &AvatarData::jointMappingLoaded, &loop, &QEventLoop::quit); - avatar->setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach fst - loop.exec();*/ // Blocking all further tests until signalled. - - // Joint geometry from fbx. //#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx" #ifdef FROM_FILE QFile file(FROM_FILE); @@ -81,8 +64,7 @@ void RigTests::initTestCase() { QCOMPARE(fbxHttpCode, 200); FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash()); #endif - //QCOMPARE(geometry.joints.count(), avatar->getJointNames().count()); - + QVector jointStates; for (int i = 0; i < geometry.joints.size(); ++i) { // Note that if the geometry is stack allocated and goes away, so will the joints. Hence the heap copy here. @@ -97,24 +79,24 @@ void RigTests::initTestCase() { std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; } -void reportJoint(int index, JointState joint) { // Handy for debugging +static void reportJoint(int index, JointState joint) { // Handy for debugging std::cout << "\n"; std::cout << index << " " << joint.getFBXJoint().name.toUtf8().data() << "\n"; std::cout << " pos:" << joint.getPosition() << "/" << joint.getPositionInParentFrame() << " from " << joint.getParentIndex() << "\n"; std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n"; std::cout << "\n"; } -void reportByName(RigPointer rig, const QString& name) { +static void reportByName(RigPointer rig, const QString& name) { int jointIndex = rig->indexOfJoint(name); reportJoint(jointIndex, rig->getJointState(jointIndex)); } -void reportAll(RigPointer rig) { +static void reportAll(RigPointer rig) { for (int i = 0; i < rig->getJointStateCount(); i++) { JointState joint = rig->getJointState(i); reportJoint(i, joint); } } -void reportSome(RigPointer rig) { +static void reportSome(RigPointer rig) { QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"}; for (auto name : names) { reportByName(rig, name); @@ -124,5 +106,4 @@ void reportSome(RigPointer rig) { void RigTests::initialPoseArmsDown() { //reportAll(_rig); reportSome(_rig); - QCOMPARE(false, true); } From 51a12adc08be27a4fccb47087f002a13cadd3b41 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 14:02:47 -0700 Subject: [PATCH 037/109] Less dependencies in tests. --- tests/rig/CMakeLists.txt | 2 +- tests/rig/src/RigTests.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/rig/CMakeLists.txt b/tests/rig/CMakeLists.txt index 5e965c3ee8..2e9dbc9424 100644 --- a/tests/rig/CMakeLists.txt +++ b/tests/rig/CMakeLists.txt @@ -1,7 +1,7 @@ # Declare dependencies macro (setup_testcase_dependencies) # link in the shared libraries - link_hifi_libraries(shared animation gpu fbx model avatars networking audio) + link_hifi_libraries(shared animation gpu fbx model) copy_dlls_beside_windows_executable() endmacro () diff --git a/tests/rig/src/RigTests.cpp b/tests/rig/src/RigTests.cpp index f1eb7cc440..0530ad5638 100644 --- a/tests/rig/src/RigTests.cpp +++ b/tests/rig/src/RigTests.cpp @@ -40,11 +40,9 @@ */ #include -#include -#include "AvatarData.h" -#include "OBJReader.h" #include "FBXReader.h" +#include "OBJReader.h" #include "AvatarRig.h" // We might later test Rig vs AvatarRig separately, but for now, we're concentrating on the main use case. #include "RigTests.h" From 98fe6dbf3deb1271e61248301a337060ef4b0afa Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 14:13:54 -0700 Subject: [PATCH 038/109] Rename tests/rig to tests/animation, per Tony. --- tests/{rig => animation}/CMakeLists.txt | 0 tests/{rig => animation}/src/RigTests.cpp | 0 tests/{rig => animation}/src/RigTests.h | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/{rig => animation}/CMakeLists.txt (100%) rename tests/{rig => animation}/src/RigTests.cpp (100%) rename tests/{rig => animation}/src/RigTests.h (100%) diff --git a/tests/rig/CMakeLists.txt b/tests/animation/CMakeLists.txt similarity index 100% rename from tests/rig/CMakeLists.txt rename to tests/animation/CMakeLists.txt diff --git a/tests/rig/src/RigTests.cpp b/tests/animation/src/RigTests.cpp similarity index 100% rename from tests/rig/src/RigTests.cpp rename to tests/animation/src/RigTests.cpp diff --git a/tests/rig/src/RigTests.h b/tests/animation/src/RigTests.h similarity index 100% rename from tests/rig/src/RigTests.h rename to tests/animation/src/RigTests.h From 064f2b471f3f3cc59fefc9e7820eab5a959f8119 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 14:29:44 -0700 Subject: [PATCH 039/109] Remove incorrectly-added file. --- interface/src/avatar/Head.cpp~ | 416 --------------------------------- 1 file changed, 416 deletions(-) delete mode 100644 interface/src/avatar/Head.cpp~ diff --git a/interface/src/avatar/Head.cpp~ b/interface/src/avatar/Head.cpp~ deleted file mode 100644 index 0dace70b3c..0000000000 --- a/interface/src/avatar/Head.cpp~ +++ /dev/null @@ -1,416 +0,0 @@ -// -// Head.cpp -// interface/src/avatar -// -// Copyright 2013 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 -#include - -#include -#include -#include - -#include "Application.h" -#include "Avatar.h" -#include "GeometryUtil.h" -#include "Head.h" -#include "Menu.h" -#include "Util.h" -#include "devices/DdeFaceTracker.h" -#include "devices/Faceshift.h" -#include "AvatarRig.h" - -using namespace std; - -Head::Head(Avatar* owningAvatar) : - HeadData((AvatarData*)owningAvatar), - _returnHeadToCenter(false), - _position(0.0f, 0.0f, 0.0f), - _rotation(0.0f, 0.0f, 0.0f), - _leftEyePosition(0.0f, 0.0f, 0.0f), - _rightEyePosition(0.0f, 0.0f, 0.0f), - _eyePosition(0.0f, 0.0f, 0.0f), - _scale(1.0f), - _lastLoudness(0.0f), - _longTermAverageLoudness(-1.0f), - _audioAttack(0.0f), - _audioJawOpen(0.0f), - _mouth2(0.0f), - _mouth3(0.0f), - _mouth4(0.0f), - _renderLookatVectors(false), - _saccade(0.0f, 0.0f, 0.0f), - _saccadeTarget(0.0f, 0.0f, 0.0f), - _leftEyeBlinkVelocity(0.0f), - _rightEyeBlinkVelocity(0.0f), - _timeWithoutTalking(0.0f), - _deltaPitch(0.0f), - _deltaYaw(0.0f), - _deltaRoll(0.0f), - _deltaLeanSideways(0.0f), - _deltaLeanForward(0.0f), - _isCameraMoving(false), - _isLookingAtMe(false), -<<<<<<< HEAD - _faceModel(this, std::make_shared()), -======= - _lookingAtMeStarted(0), - _wasLastLookingAtMe(0), - _faceModel(this), ->>>>>>> 8a34df380cac67142dbb30bc20e8e022fdd551e0 - _leftEyeLookAtID(DependencyManager::get()->allocateID()), - _rightEyeLookAtID(DependencyManager::get()->allocateID()) -{ -} - -void Head::init() { - _faceModel.init(); -} - -void Head::reset() { - _baseYaw = _basePitch = _baseRoll = 0.0f; - _leanForward = _leanSideways = 0.0f; - _faceModel.reset(); -} - -void Head::simulate(float deltaTime, bool isMine, bool billboard) { - // Update audio trailing average for rendering facial animations - const float AUDIO_AVERAGING_SECS = 0.05f; - const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; - _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); - - if (_longTermAverageLoudness == -1.0f) { - _longTermAverageLoudness = _averageLoudness; - } else { - _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); - } - - if (isMine) { - MyAvatar* myAvatar = static_cast(_owningAvatar); - - // Only use face trackers when not playing back a recording. - if (!myAvatar->isPlaying()) { - FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker(); - _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); - if (_isFaceTrackerConnected) { - _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - - if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { - - if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { - calculateMouthShapes(); - - const int JAW_OPEN_BLENDSHAPE = 21; - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; - _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; - _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; - } - - applyEyelidOffset(getFinalOrientationInWorldFrame()); - } - } - } - // Twist the upper body to follow the rotation of the head, but only do this with my avatar, - // since everyone else will see the full joint rotations for other people. - const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f; - const float BODY_FOLLOW_HEAD_FACTOR = 0.66f; - float currentTwist = getTorsoTwist(); - setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE); - } - - if (!(_isFaceTrackerConnected || billboard)) { - // Update eye saccades - const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; - const float AVERAGE_SACCADE_INTERVAL = 6.0f; - const float MICROSACCADE_MAGNITUDE = 0.002f; - const float SACCADE_MAGNITUDE = 0.04f; - const float NOMINAL_FRAME_RATE = 60.0f; - - if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { - _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); - } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { - _saccadeTarget = SACCADE_MAGNITUDE * randVector(); - } - _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); - - // Detect transition from talking to not; force blink after that and a delay - bool forceBlink = false; - const float TALKING_LOUDNESS = 100.0f; - const float BLINK_AFTER_TALKING = 0.25f; - if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { - _timeWithoutTalking = 0.0f; - - } else if (_timeWithoutTalking < BLINK_AFTER_TALKING && (_timeWithoutTalking += deltaTime) >= BLINK_AFTER_TALKING) { - forceBlink = true; - } - - // Update audio attack data for facial animation (eyebrows and mouth) - const float AUDIO_ATTACK_AVERAGING_RATE = 0.9f; - _audioAttack = AUDIO_ATTACK_AVERAGING_RATE * _audioAttack + (1.0f - AUDIO_ATTACK_AVERAGING_RATE) * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness); - _lastLoudness = (_audioLoudness - _longTermAverageLoudness); - - const float BROW_LIFT_THRESHOLD = 100.0f; - if (_audioAttack > BROW_LIFT_THRESHOLD) { - _browAudioLift += sqrtf(_audioAttack) * 0.01f; - } - _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); - - const float BLINK_SPEED = 10.0f; - const float BLINK_SPEED_VARIABILITY = 1.0f; - const float BLINK_START_VARIABILITY = 0.25f; - const float FULLY_OPEN = 0.0f; - const float FULLY_CLOSED = 1.0f; - if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { - // no blinking when brows are raised; blink less with increasing loudness - const float BASE_BLINK_RATE = 15.0f / 60.0f; - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * - ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { - _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - if (randFloat() < 0.5f) { - _leftEyeBlink = BLINK_START_VARIABILITY; - } else { - _rightEyeBlink = BLINK_START_VARIABILITY; - } - } - } else { - _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - - if (_leftEyeBlink == FULLY_CLOSED) { - _leftEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_leftEyeBlink == FULLY_OPEN) { - _leftEyeBlinkVelocity = 0.0f; - } - if (_rightEyeBlink == FULLY_CLOSED) { - _rightEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_rightEyeBlink == FULLY_OPEN) { - _rightEyeBlinkVelocity = 0.0f; - } - } - - // use data to update fake Faceshift blendshape coefficients - calculateMouthShapes(); - DependencyManager::get()->updateFakeCoefficients(_leftEyeBlink, - _rightEyeBlink, - _browAudioLift, - _audioJawOpen, - _mouth2, - _mouth3, - _mouth4, - _blendshapeCoefficients); - - applyEyelidOffset(getOrientation()); - - } else { - _saccade = glm::vec3(); - } - - if (!isMine) { - _faceModel.setLODDistance(static_cast(_owningAvatar)->getLODDistance()); - } - _leftEyePosition = _rightEyePosition = getPosition(); - if (!billboard) { - _faceModel.simulate(deltaTime); - if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) { - static_cast(_owningAvatar)->getSkeletonModel().getEyePositions(_leftEyePosition, _rightEyePosition); - } - } - _eyePosition = calculateAverageEyePosition(); -} - -void Head::calculateMouthShapes() { - const float JAW_OPEN_SCALE = 0.015f; - const float JAW_OPEN_RATE = 0.9f; - const float JAW_CLOSE_RATE = 0.90f; - float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE; - if (audioDelta > _audioJawOpen) { - _audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE; - } else { - _audioJawOpen *= JAW_CLOSE_RATE; - } - _audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f); - - // _mouth2 = "mmmm" shape - // _mouth3 = "funnel" shape - // _mouth4 = "smile" shape - const float FUNNEL_PERIOD = 0.985f; - const float FUNNEL_RANDOM_PERIOD = 0.01f; - const float MMMM_POWER = 0.25f; - const float MMMM_PERIOD = 0.91f; - const float MMMM_RANDOM_PERIOD = 0.15f; - const float SMILE_PERIOD = 0.925f; - const float SMILE_RANDOM_PERIOD = 0.05f; - - _mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD); - _mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD); - _mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD); -} - -void Head::applyEyelidOffset(glm::quat headOrientation) { - // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. - - glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getCorrectedLookAtPosition() - _eyePosition); - eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head - float eyePitch = safeEulerAngles(eyeRotation).x; - - const float EYE_PITCH_TO_COEFFICIENT = 1.6f; // Empirically determined - const float MAX_EYELID_OFFSET = 0.8f; // So that don't fully close eyes when looking way down - float eyelidOffset = glm::clamp(-eyePitch * EYE_PITCH_TO_COEFFICIENT, -1.0f, MAX_EYELID_OFFSET); - - for (int i = 0; i < 2; i++) { - const int LEFT_EYE = 8; - float eyeCoefficient = _blendshapeCoefficients[i] - _blendshapeCoefficients[LEFT_EYE + i]; // Raw value - eyeCoefficient = glm::clamp(eyelidOffset + eyeCoefficient * (1.0f - eyelidOffset), -1.0f, 1.0f); - if (eyeCoefficient > 0.0f) { - _blendshapeCoefficients[i] = eyeCoefficient; - _blendshapeCoefficients[LEFT_EYE + i] = 0.0f; - - } else { - _blendshapeCoefficients[i] = 0.0f; - _blendshapeCoefficients[LEFT_EYE + i] = -eyeCoefficient; - } - } -} - -void Head::relaxLean(float deltaTime) { - // restore rotation, lean to neutral positions - const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds - float relaxationFactor = 1.0f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.0f); - _deltaYaw *= relaxationFactor; - _deltaPitch *= relaxationFactor; - _deltaRoll *= relaxationFactor; - _leanSideways *= relaxationFactor; - _leanForward *= relaxationFactor; - _deltaLeanSideways *= relaxationFactor; - _deltaLeanForward *= relaxationFactor; -} - -void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) { - if (_renderLookatVectors) { - renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); - } -} - -void Head::setScale (float scale) { - if (_scale == scale) { - return; - } - _scale = scale; -} - -glm::quat Head::getFinalOrientationInWorldFrame() const { - return _owningAvatar->getOrientation() * getFinalOrientationInLocalFrame(); -} - -glm::quat Head::getFinalOrientationInLocalFrame() const { - return glm::quat(glm::radians(glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() ))); -} - -glm::vec3 Head::getCorrectedLookAtPosition() { - if (isLookingAtMe()) { - return _correctedLookAtPosition; - } else { - return getLookAtPosition(); - } -} - -void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { - 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 - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're - // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not - // always the same. - if (qApp->isHMDMode()) { - return getOrientation(); - } - Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); -} - -glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { - glm::quat orientation = getOrientation(); - glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; - return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation; -} - -glm::vec3 Head::getScalePivot() const { - return _faceModel.isActive() ? _faceModel.getTranslation() : _position; -} - -void Head::setFinalPitch(float finalPitch) { - _deltaPitch = glm::clamp(finalPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH) - _basePitch; -} - -void Head::setFinalYaw(float finalYaw) { - _deltaYaw = glm::clamp(finalYaw, MIN_HEAD_YAW, MAX_HEAD_YAW) - _baseYaw; -} - -void Head::setFinalRoll(float finalRoll) { - _deltaRoll = glm::clamp(finalRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL) - _baseRoll; -} - -float Head::getFinalYaw() const { - return glm::clamp(_baseYaw + _deltaYaw, MIN_HEAD_YAW, MAX_HEAD_YAW); -} - -float Head::getFinalPitch() const { - return glm::clamp(_basePitch + _deltaPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); -} - -float Head::getFinalRoll() const { - return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); -} - -void Head::addLeanDeltas(float sideways, float forward) { - _deltaLeanSideways += sideways; - _deltaLeanForward += forward; -} - -void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { - auto& batch = *renderArgs->_batch; - auto transform = Transform{}; - batch.setModelTransform(transform); - batch._glLineWidth(2.0f); - - auto deferredLighting = DependencyManager::get(); - deferredLighting->bindSimpleProgram(batch); - - auto geometryCache = DependencyManager::get(); - glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f); - glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f); - geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID); - geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); -} - - From 1bb734aec0bbf6e1eb4e0b64627832307d6d8e16 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 27 Jul 2015 15:26:03 -0700 Subject: [PATCH 040/109] Kill warnings. --- interface/src/avatar/Head.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 6 +++++- interface/src/avatar/SkeletonModel.h | 3 ++- libraries/animation/src/Rig.h | 2 +- libraries/render-utils/src/Model.cpp | 8 ++++---- libraries/render-utils/src/Model.h | 5 +++-- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b9c453421b..bfa2e437a1 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -56,9 +56,9 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), - _faceModel(this, std::make_shared()), _lookingAtMeStarted(0), _wasLastLookingAtMe(0), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 798b3c628d..7b6fb85ae5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -115,6 +115,10 @@ void SkeletonModel::updateClusterMatrices() { } } +void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + _rig->simulateInternal(deltaTime, parentTransform, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); +} + void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getSkeletonPosition()); static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -122,7 +126,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients()); - Model::simulate(deltaTime, fullUpdate, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + Model::simulate(deltaTime, fullUpdate); if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 89c7ab1fcb..10b811415e 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -29,7 +29,8 @@ public: virtual void initJointStates(QVector states); - void simulate(float deltaTime, bool fullUpdate = true); + virtual void simulate(float deltaTime, bool fullUpdate = true); + virtual void updateRig(float deltaTime, glm::mat4 parentTransform); void renderIKConstraints(gpu::Batch& batch); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 379ed64877..8b2fc10fa5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -132,7 +132,7 @@ public: virtual bool getIsFirstPerson() const { return _isFirstPerson; } bool getJointsAreDirty() { return _jointsAreDirty; } - bool setEnableRig(bool isEnabled) { _enableRig = isEnabled; } + void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } protected: QVector _jointStates; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index dc1051dad3..dd2d3369c8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1301,7 +1301,7 @@ void Model::snapToRegistrationPoint() { _snappedToRegistrationPoint = true; } -void Model::simulate(float deltaTime, bool fullUpdate, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { +void Model::simulate(float deltaTime, bool fullUpdate) { PROFILE_RANGE(__FUNCTION__); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); @@ -1322,16 +1322,16 @@ void Model::simulate(float deltaTime, bool fullUpdate, const glm::vec3& worldPos if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) { snapToRegistrationPoint(); } - simulateInternal(deltaTime, worldPosition, worldVelocity, worldRotation); + simulateInternal(deltaTime); } } -void Model::simulateInternal(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { +void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->simulateInternal(deltaTime, parentTransform, worldPosition, worldVelocity, worldRotation); + updateRig(deltaTime, parentTransform); glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index a7b81abea9..27ef808ef0 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -115,7 +115,7 @@ public: void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } - virtual void simulate(float deltaTime, bool fullUpdate = true, const glm::vec3& worldPosition = glm::vec3(), const glm::vec3& worldVelocity = glm::vec3(), const glm::quat& worldRotation = glm::quat()); + virtual void simulate(float deltaTime, bool fullUpdate = true); /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -268,7 +268,8 @@ protected: void scaleToFit(); void snapToRegistrationPoint(); - void simulateInternal(float deltaTime, const glm::vec3& worldPosition = glm::vec3(), const glm::vec3& worldVelocity = glm::vec3(), const glm::quat& worldRotation = glm::quat()); + void simulateInternal(float deltaTime); + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) {}; // Subclasses may be more interesting /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); From 5ae15d46b40877c6cac412382ef6bbda56e26637 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Jul 2015 16:42:54 -0700 Subject: [PATCH 041/109] add some javascript callable stuff back to MyAvatar --- interface/src/avatar/MyAvatar.cpp | 29 ++++++++++++++++++- interface/src/avatar/MyAvatar.h | 5 ++++ interface/src/avatar/SkeletonModel.h | 42 ++++++++++++++-------------- libraries/render-utils/src/Model.cpp | 4 --- libraries/render-utils/src/Model.h | 5 ---- 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fe4d4bc3cb..f29c21f114 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -900,10 +900,37 @@ glm::vec3 MyAvatar::getDefaultEyePosition() const { const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f; +void MyAvatar::setJointRotations(QVector jointRotations) { + int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size()); + for (int i = 0; i < numStates; ++i) { + // HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here + _skeletonModel.setJointState(i, true, jointRotations[i], RECORDER_PRIORITY); + } +} + +void MyAvatar::setJointData(int index, const glm::quat& rotation) { + if (QThread::currentThread() == thread()) { + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, true, rotation, SCRIPT_PRIORITY); + } +} + +void MyAvatar::clearJointData(int index) { + if (QThread::currentThread() == thread()) { + // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, false, glm::quat(), 0.0f); + _rig->clearJointAnimationPriority(index); + } +} + +void MyAvatar::clearJointsData() { + clearJointAnimationPriorities(); +} + void MyAvatar::clearJointAnimationPriorities() { int numStates = _skeletonModel.getJointStateCount(); for (int i = 0; i < numStates; ++i) { - _skeletonModel.clearJointAnimationPriority(i); + _rig->clearJointAnimationPriority(i); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e83deddf16..4dfe0611af 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -104,6 +104,11 @@ public: void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); + virtual void setJointRotations(QVector jointRotations); + virtual void setJointData(int index, const glm::quat& rotation); + virtual void clearJointData(int index); + virtual void clearJointsData(); + Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString()); Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString()); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 14dc2da3f0..5d76ac1149 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -22,68 +22,68 @@ class MuscleConstraint; /// A skeleton loaded from a model. class SkeletonModel : public Model { Q_OBJECT - + public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - + virtual void initJointStates(QVector states); void simulate(float deltaTime, bool fullUpdate = true); void renderIKConstraints(gpu::Batch& batch); - + /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } - + /// Returns the index of the right hand joint, or -1 if not found. int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; } /// Retrieve the position of the left hand /// \return true whether or not the position was found bool getLeftHandPosition(glm::vec3& position) const; - + /// Retrieve the position of the right hand /// \return true whether or not the position was found bool getRightHandPosition(glm::vec3& position) const; - + /// Restores some fraction of the default position of the left hand. /// \param fraction the fraction of the default position to restore /// \return whether or not the left hand joint was found bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f); - + /// Gets the position of the left shoulder. /// \return whether or not the left shoulder joint was found bool getLeftShoulderPosition(glm::vec3& position) const; - + /// Returns the extended length from the left hand to its last free ancestor. float getLeftArmLength() const; - + /// Restores some fraction of the default position of the right hand. /// \param fraction the fraction of the default position to restore /// \return whether or not the right hand joint was found bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f); - + /// Gets the position of the right shoulder. /// \return whether or not the right shoulder joint was found bool getRightShoulderPosition(glm::vec3& position) const; - + /// Returns the extended length from the right hand to its first free ancestor. float getRightArmLength() const; /// Returns the position of the head joint. /// \return whether or not the head was found bool getHeadPosition(glm::vec3& headPosition) const; - + /// Returns the position of the neck joint. /// \return whether or not the neck was found bool getNeckPosition(glm::vec3& neckPosition) const; - + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; - + /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; @@ -119,12 +119,12 @@ protected: /// \param jointIndex index of joint in model /// \param position position of joint in model-frame void applyHandPosition(int jointIndex, const glm::vec3& position); - + void applyPalmData(int jointIndex, PalmData& palm); - + /// Updates the state of the joint at the specified index. - virtual void updateJointState(int index); - + virtual void updateJointState(int index); + void maybeUpdateLeanRotation(const JointState& parentState, int index); void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index); @@ -137,9 +137,9 @@ protected: private: void renderJointConstraints(gpu::Batch& batch, int jointIndex); - void renderOrientationDirections(gpu::Batch& batch, int jointIndex, + void renderOrientationDirections(gpu::Batch& batch, int jointIndex, glm::vec3 position, const glm::quat& orientation, float size); - + struct OrientationLineIDs { int _up; int _front; @@ -154,7 +154,7 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + Avatar* _owningAvatar; CapsuleShape _boundingShape; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a316da0f99..1f7b67ef05 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1052,10 +1052,6 @@ void Model::clearJointState(int index) { _rig->clearJointState(index); } -void Model::clearJointAnimationPriority(int index) { - _rig->clearJointAnimationPriority(index); -} - void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { _rig->setJointState(index, valid, rotation, priority); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 45d7ce63ab..fc8347581b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -150,10 +149,6 @@ public: /// Sets the distance parameter used for LOD computations. void setLODDistance(float distance) { _lodDistance = distance; } - const QList& getRunningAnimations() const { return _rig->getRunningAnimations(); } - /// Clear the joint animation priority - void clearJointAnimationPriority(int index); - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled From 4754615159f5c4f554181d59e67cc367db2177c2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 28 Jul 2015 12:34:10 -0700 Subject: [PATCH 042/109] Fix NPC animations. --- interface/src/avatar/SkeletonModel.cpp | 3 ++- libraries/animation/src/Rig.cpp | 5 +++-- libraries/animation/src/Rig.h | 5 ++++- libraries/render-utils/src/Model.cpp | 4 ++++ libraries/render-utils/src/Model.h | 2 +- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7b6fb85ae5..636e58c5a8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -116,7 +116,8 @@ void SkeletonModel::updateClusterMatrices() { } void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - _rig->simulateInternal(deltaTime, parentTransform, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + Model::updateRig(deltaTime, parentTransform); } void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d20abc58d6..0bd3f14096 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -341,7 +341,7 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { return maybeCauterizeHead(jointIndex).getVisibleTransform(); } -void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { +void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { if (_enableRig) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -374,8 +374,9 @@ void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm _isTurning = isTurning; _isIdle = isIdle; } +} - // update animations +void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { foreach (const AnimationHandlePointer& handle, _runningAnimations) { handle->simulate(deltaTime); } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 8b2fc10fa5..52d5866369 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -108,7 +108,10 @@ public: void setJointTransform(int jointIndex, glm::mat4 newTransform); glm::mat4 getJointVisibleTransform(int jointIndex) const; void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); - void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); + // Start or stop animations as needed. + void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); + // Regardless of who started the animations or how many, update the joints. + void updateAnimations(float deltaTime, glm::mat4 parentTransform); bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, const QVector& freeLineage, glm::mat4 parentTransform); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index dd2d3369c8..92e49fdc55 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1326,6 +1326,10 @@ void Model::simulate(float deltaTime, bool fullUpdate) { } } +//virtual +void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { + _rig->updateAnimations(deltaTime, parentTransform); +} void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 27ef808ef0..83527969b2 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -269,7 +269,7 @@ protected: void snapToRegistrationPoint(); void simulateInternal(float deltaTime); - virtual void updateRig(float deltaTime, glm::mat4 parentTransform) {}; // Subclasses may be more interesting + virtual void updateRig(float deltaTime, glm::mat4 parentTransform); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); From 8b20f9d3a68a31450c95a6dbb90f51aa9fca8837 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 28 Jul 2015 15:48:01 -0700 Subject: [PATCH 043/109] do the minimum include to use glew on linux --- libraries/gpu/src/gpu/GLBackend.cpp | 7 +++++++ libraries/gpu/src/gpu/GPUConfig.h | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 6b1d552be9..3942af4f84 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -115,6 +115,13 @@ GLBackend::GLBackend() : #endif #if defined(Q_OS_LINUX) + GLenum err = glewInit(); + if (GLEW_OK != err) { + /* Problem: glewInit failed, something is seriously wrong. */ + qCDebug(gpulogging, "Error: %s\n", glewGetErrorString(err)); + } + qCDebug(gpulogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); + // TODO: Write the correct code for Linux... /* if (wglewGetExtension("WGL_EXT_swap_control")) { int swapInterval = wglGetSwapIntervalEXT(); diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 1d092dbc6a..a9657076fa 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -34,8 +34,11 @@ #elif defined(ANDROID) #else -#include -#include + +#include +#include +//#include +//#include #define GPU_FEATURE_PROFILE GPU_LEGACY #define GPU_TRANSFORM_PROFILE GPU_LEGACY From 27d3d3f450cc904636cfe55e13a4a1d053923c82 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 28 Jul 2015 15:51:15 -0700 Subject: [PATCH 044/109] fix w to x --- libraries/gpu/src/gpu/GPUConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index a9657076fa..2e3149919c 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,7 +36,7 @@ #else #include -#include +#include //#include //#include From b06485c266e783a1088d2f597a91ac1a22d17dd2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Jul 2015 17:41:45 -0700 Subject: [PATCH 045/109] Remove cauterize code from Rig and move it back into Model. * cauterize code is used as at render time and is not dependent on the jointStates. * MyAvatar now initialize the bone set used for cauterization and makes the decision to perform cauterization or not in preRender. --- interface/src/avatar/MyAvatar.cpp | 42 +++++++++++--- interface/src/avatar/MyAvatar.h | 4 +- interface/src/avatar/SkeletonModel.cpp | 10 +--- libraries/animation/src/Rig.cpp | 79 +++++--------------------- libraries/animation/src/Rig.h | 13 +---- libraries/render-utils/src/Model.cpp | 41 ++++++++++--- libraries/render-utils/src/Model.h | 16 +++++- 7 files changed, 98 insertions(+), 107 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5f0b5f03da..c995131427 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -969,12 +969,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); render::ScenePointer scene = Application::getInstance()->getMain3DScene(); _billboardValid = false; - - if (_useFullAvatar) { - _skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene); - } else { - _skeletonModel.setVisibleInScene(true, scene); - } + _skeletonModel.setVisibleInScene(true, scene); + _headBoneSet.clear(); } void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { @@ -1184,17 +1180,45 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene } } +void MyAvatar::initHeadBones() { + int neckJointIndex = -1; + if (_skeletonModel.getGeometry()) { + neckJointIndex = _skeletonModel.getGeometry()->getFBXGeometry().neckJointIndex; + } + if (neckJointIndex == -1) { + return; + } + _headBoneSet.clear(); + std::queue q; + q.push(neckJointIndex); + _headBoneSet.insert(neckJointIndex); + + // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + while (q.size() > 0) { + int jointIndex = q.front(); + for (int i = 0; i < _skeletonModel.getJointStateCount(); i++) { + if (jointIndex == _skeletonModel.getParentJointIndex(i)) { + _headBoneSet.insert(i); + q.push(i); + } + } + q.pop(); + } +} + void MyAvatar::preRender(RenderArgs* renderArgs) { render::ScenePointer scene = Application::getInstance()->getMain3DScene(); const bool shouldDrawHead = shouldRenderHead(renderArgs); - _skeletonModel.initWhenReady(scene); + if (_skeletonModel.initWhenReady(scene)) { + initHeadBones(); + _skeletonModel.setCauterizeBoneSet(_headBoneSet); + } if (shouldDrawHead != _prevShouldDrawHead) { if (_useFullAvatar) { - _skeletonModel.setVisibleInScene(true, scene); - _rig->setFirstPerson(!shouldDrawHead); + _skeletonModel.setCauterizeBones(!shouldDrawHead); } else { getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 802c92ec2c..67097a8f3b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -273,7 +273,8 @@ private: void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); - + void initHeadBones(); + // Avatar Preferences bool _useFullAvatar = false; QUrl _fullAvatarURLFromPreferences; @@ -286,6 +287,7 @@ private: RigPointer _rig; bool _prevShouldDrawHead; + std::unordered_set _headBoneSet; }; #endif // hifi_MyAvatar_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 636e58c5a8..122559bedb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -51,7 +51,7 @@ SkeletonModel::~SkeletonModel() { void SkeletonModel::initJointStates(QVector states) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _boundingRadius = _rig->initJointStates(states, parentTransform, geometry.neckJointIndex); + _boundingRadius = _rig->initJointStates(states, parentTransform); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; @@ -175,14 +175,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } } - - // if (_isFirstPerson) { - // cauterizeHead(); - // updateClusterMatrices(); - // } - if (_rig->getJointsAreDirty()) { - updateClusterMatrices(); - } } void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0bd3f14096..302211b556 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -124,9 +124,8 @@ void Rig::deleteAnimations() { _animationHandles.clear(); } -float Rig::initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex) { +float Rig::initJointStates(QVector states, glm::mat4 parentTransform) { _jointStates = states; - _neckJointIndex = neckJointIndex; initJointTransforms(parentTransform); int numStates = _jointStates.size(); @@ -142,8 +141,6 @@ float Rig::initJointStates(QVector states, glm::mat4 parentTransform _jointStates[i].slaveVisibleTransform(); } - initHeadBones(); - return radius; } @@ -195,8 +192,7 @@ JointState Rig::getJointState(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return JointState(); } - // return _jointStates[jointIndex]; - return maybeCauterizeHead(jointIndex); + return _jointStates[jointIndex]; } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { @@ -270,8 +266,7 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, return false; } // position is in world-frame - // position = translation + rotation * _jointStates[jointIndex].getPosition(); - position = translation + rotation * maybeCauterizeHead(jointIndex).getPosition(); + position = translation + rotation * _jointStates[jointIndex].getPosition(); return true; } @@ -280,7 +275,7 @@ bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { return false; } // position is in model-frame - position = extractTranslation(maybeCauterizeHead(jointIndex).getTransform()); + position = extractTranslation(_jointStates[jointIndex].getTransform()); return true; } @@ -288,7 +283,7 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * maybeCauterizeHead(jointIndex).getRotation(); + result = rotation * _jointStates[jointIndex].getRotation(); return true; } @@ -296,7 +291,7 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - rotation = maybeCauterizeHead(jointIndex).getRotation(); + rotation = _jointStates[jointIndex].getRotation(); return true; } @@ -304,7 +299,7 @@ bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm: if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * maybeCauterizeHead(jointIndex).getRotation(); + result = rotation * _jointStates[jointIndex].getRotation(); return true; } @@ -315,7 +310,7 @@ bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& positio return false; } // position is in world-frame - position = translation + rotation * maybeCauterizeHead(jointIndex).getVisiblePosition(); + position = translation + rotation * _jointStates[jointIndex].getVisiblePosition(); return true; } @@ -323,7 +318,7 @@ bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; } - result = rotation * maybeCauterizeHead(jointIndex).getVisibleRotation(); + result = rotation * _jointStates[jointIndex].getVisibleRotation(); return true; } @@ -331,14 +326,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } - return maybeCauterizeHead(jointIndex).getTransform(); + return _jointStates[jointIndex].getTransform(); } glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } - return maybeCauterizeHead(jointIndex).getVisibleTransform(); + return _jointStates[jointIndex].getVisibleTransform(); } void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { @@ -624,7 +619,7 @@ glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { if (jointIndex == -1 || _jointStates.isEmpty()) { return glm::vec3(); } - return maybeCauterizeHead(jointIndex).getDefaultTranslationInConstrainedFrame(); + return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); } glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { @@ -669,53 +664,5 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { if (jointIndex == -1 || _jointStates.isEmpty()) { return glm::quat(); } - return maybeCauterizeHead(jointIndex).getDefaultRotationInParentFrame(); -} - -void Rig::initHeadBones() { - if (_neckJointIndex == -1) { - return; - } - _headBones.clear(); - std::queue q; - q.push(_neckJointIndex); - _headBones.push_back(_neckJointIndex); - - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. - while (q.size() > 0) { - int jointIndex = q.front(); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint(); - if (jointIndex == fbxJoint.parentIndex) { - _headBones.push_back(i); - q.push(i); - } - } - q.pop(); - } -} - -JointState Rig::maybeCauterizeHead(int jointIndex) const { - // if (_headBones.contains(jointIndex)) { - // XXX fix this... make _headBones a hash? add a flag to JointState? - if (_neckJointIndex != -1 && - _isFirstPerson && - std::find(_headBones.begin(), _headBones.end(), jointIndex) != _headBones.end()) { - glm::vec4 trans = _jointStates[jointIndex].getTransform()[3]; - glm::vec4 zero(0, 0, 0, 0); - glm::mat4 newXform(zero, zero, zero, trans); - JointState jointStateCopy = _jointStates[jointIndex]; - jointStateCopy.setTransform(newXform); - jointStateCopy.setVisibleTransform(newXform); - return jointStateCopy; - } else { - return _jointStates[jointIndex]; - } -} - -void Rig::setFirstPerson(bool isFirstPerson) { - if (_isFirstPerson != isFirstPerson) { - _isFirstPerson = isFirstPerson; - _jointsAreDirty = true; - } + return _jointStates[jointIndex].getDefaultRotationInParentFrame(); } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 52d5866369..fe6bc82f35 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,7 +75,7 @@ public: float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false); - float initJointStates(QVector states, glm::mat4 parentTransform, int neckJointIndex); + float initJointStates(QVector states, glm::mat4 parentTransform); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; int getJointStateCount() const { return _jointStates.size(); } int indexOfJoint(const QString& jointName) ; @@ -131,10 +131,6 @@ public: virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0; - virtual void setFirstPerson(bool isFirstPerson); - virtual bool getIsFirstPerson() const { return _isFirstPerson; } - - bool getJointsAreDirty() { return _jointsAreDirty; } void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } protected: @@ -143,13 +139,6 @@ public: QList _animationHandles; QList _runningAnimations; - JointState maybeCauterizeHead(int jointIndex) const; - void initHeadBones(); - bool _isFirstPerson = false; - std::vector _headBones; - bool _jointsAreDirty = false; - int _neckJointIndex = -1; - bool _enableRig; bool _isWalking; bool _isTurning; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 92e49fdc55..803658a667 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -67,6 +67,7 @@ Model::Model(RigPointer rig, QObject* parent) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), + _cauterizeBones(false), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), @@ -452,6 +453,7 @@ bool Model::updateGeometry() { foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); + state.cauterizedClusterMatrices.resize(mesh.clusters.size()); _meshStates.append(state); auto buffer = std::make_shared(); @@ -472,7 +474,7 @@ bool Model::updateGeometry() { void Model::initJointStates(QVector states) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _boundingRadius = _rig->initJointStates(states, parentTransform, geometry.neckJointIndex); + _boundingRadius = _rig->initJointStates(states, parentTransform); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -1337,6 +1339,12 @@ void Model::simulateInternal(float deltaTime) { glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; updateRig(deltaTime, parentTransform); + glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; + glm::mat4 modelToWorld = glm::mat4_cast(_rotation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; @@ -1344,14 +1352,30 @@ void Model::simulateInternal(float deltaTime) { if (_showTrueJointTransforms) { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; + auto jointMatrix =_rig->getJointTransform(cluster.jointIndex); + state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + } } } else { for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; + auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex); + state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + + // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. + if (!_cauterizeBoneSet.empty()) { + if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { + jointMatrix = cauterizeMatrix; + } + state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + } } } } @@ -1611,8 +1635,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (isSkinned) { - batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, - (const float*)state.clusterMatrices.constData()); + const float* bones = (const float*)state.clusterMatrices.constData(); + if (_cauterizeBones) { + bones = (const float*)state.cauterizedClusterMatrices.constData(); + } + batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, bones); _transforms[0] = Transform(); _transforms[0].preTranslate(_translation); } else { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 83527969b2..1dab3f37a1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -172,6 +173,9 @@ public: /// \return true if joint exists bool getJointRotation(int jointIndex, glm::quat& rotation) const; + /// Returns the index of the parent of the indexed joint, or -1 if not found. + int getParentJointIndex(int jointIndex) const; + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); /// Returns the extents of the model in its bind pose. @@ -187,6 +191,12 @@ public: bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to + void setCauterizeBones(bool flag) { _cauterizeBones = flag; } + bool getCauterizeBones() const { return _cauterizeBones; } + + const std::unordered_set& getCauterizeBoneSet() const { return _cauterizeBoneSet; } + void setCauterizeBoneSet(const std::unordered_set& boneSet) { _cauterizeBoneSet = boneSet; } + protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; } @@ -218,9 +228,6 @@ protected: /// Clear the joint states void clearJointState(int index); - /// Returns the index of the parent of the indexed joint, or -1 if not found. - int getParentJointIndex(int jointIndex) const; - /// Returns the index of the last free ancestor of the indexed joint, or -1 if not found. int getLastFreeJointIndex(int jointIndex) const; @@ -255,9 +262,12 @@ protected: class MeshState { public: QVector clusterMatrices; + QVector cauterizedClusterMatrices; }; QVector _meshStates; + std::unordered_set _cauterizeBoneSet; + bool _cauterizeBones; // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); From 77a12eb50ea7af97a46060e178c2d6479af0bdc0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jul 2015 17:53:01 -0700 Subject: [PATCH 046/109] compile on linux with GLEW --- cmake/modules/FindGLEW.cmake | 14 +++++++ interface/CMakeLists.txt | 6 +++ interface/src/Stars.cpp | 10 ++--- interface/src/devices/OculusManager.cpp | 6 +-- interface/src/devices/TV3DManager.cpp | 9 +++-- interface/src/devices/TV3DManager.h | 1 + .../src/RenderablePolyVoxEntityItem.cpp | 2 +- .../src/RenderableWebEntityItem.cpp | 3 +- libraries/gpu/CMakeLists.txt | 7 +++- libraries/gpu/src/gpu/Batch.cpp | 6 +-- libraries/gpu/src/gpu/GLBackend.cpp | 3 +- libraries/gpu/src/gpu/GLBackendQuery.cpp | 1 - libraries/gpu/src/gpu/GLBackendShader.cpp | 1 - libraries/gpu/src/gpu/GLBackendShared.h | 5 ++- libraries/gpu/src/gpu/GPUConfig.h | 2 +- libraries/model/src/model/Material.h | 2 +- .../src/AmbientOcclusionEffect.cpp | 6 +-- .../src/DeferredLightingEffect.cpp | 38 +++++++++--------- libraries/render-utils/src/GeometryCache.cpp | 10 ++--- libraries/render-utils/src/Model.cpp | 8 ++-- .../render-utils/src/RenderDeferredTask.cpp | 4 +- libraries/render-utils/src/TextureCache.cpp | 7 ++-- libraries/render/src/render/DrawStatus.cpp | 7 ++-- libraries/render/src/render/DrawTask.cpp | 6 +-- tests/render-utils/src/main.cpp | 39 ++++++++++--------- 25 files changed, 109 insertions(+), 94 deletions(-) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index e86db3fdac..b1789fb614 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -38,5 +38,19 @@ if (WIN32) find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_DLL_PATH) add_paths_to_fixup_libs(${GLEW_DLL_PATH}) +elseif (APPLE) +else () + find_path(GLEW_INCLUDE_DIR GL/glew.h) + find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew32s PATH_SUFFIXES lib64) + + set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR}) + set(GLEW_LIBRARIES ${GLEW_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(GLEW + REQUIRED_VARS GLEW_INCLUDE_DIR GLEW_LIBRARY) + + mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY) + endif () diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0c44ac801f..6af3cfa08b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -245,6 +245,12 @@ else (APPLE) endif () endif() + else (WIN32) + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) + + target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES}) + message(STATUS ${GLEW_LIBRARY}) endif() endif (APPLE) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index ebfddee38c..7b612acb68 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -14,8 +14,6 @@ #include #include -#include -#include #include #include #include @@ -207,7 +205,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch._glUniform1f(_timeSlot, secs); geometryCache->renderUnitCube(batch); - glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); static const size_t VERTEX_STRIDE = sizeof(StarVertex); size_t offset = offsetof(StarVertex, position); @@ -217,9 +215,9 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { // Render the stars batch.setPipeline(_starsPipeline); - batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); - batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - batch._glEnable(GL_POINT_SMOOTH); + //batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); + //batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + //batch._glEnable(GL_POINT_SMOOTH); batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, posView); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c9c70b4417..16685df96f 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -11,16 +11,16 @@ // #include "OculusManager.h" -#include +#include #include #include +#include #include +#include #include #include -#include -#include #include #include diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index fefaf060bd..5dee5988c1 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -9,13 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "TV3DManager.h" + #include #include -#include "gpu/GLBackend.h" -#include "Application.h" +#include -#include "TV3DManager.h" +#include "Application.h" #include "Menu.h" int TV3DManager::_screenWidth = 1; @@ -63,6 +64,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) { } void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) { +#ifdef THIS_CURRENTLY_BROKEN_WAITING_FOR_DISPLAY_PLUGINS if (screenHeight == 0) { screenHeight = 1; // prevent divide by 0 } @@ -72,6 +74,7 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre setFrustum(whichCamera); glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport +#endif } void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { diff --git a/interface/src/devices/TV3DManager.h b/interface/src/devices/TV3DManager.h index 330a4ee0ee..96ee79f7d1 100644 --- a/interface/src/devices/TV3DManager.h +++ b/interface/src/devices/TV3DManager.h @@ -17,6 +17,7 @@ #include class Camera; +class RenderArgs; struct eyeFrustum { double left; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d3ee312311..681702bb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -35,7 +35,7 @@ #include #include "model/Geometry.h" -#include "gpu/GLBackend.h" +#include "gpu/Context.h" #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 7fa615073b..9e48164647 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -8,7 +8,6 @@ #include "RenderableWebEntityItem.h" -#include #include #include #include @@ -24,7 +23,7 @@ #include #include #include -#include +#include #include "EntityTreeRenderer.h" diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 30949b83e1..eda168091e 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -31,13 +31,16 @@ elseif (WIN32) elseif (ANDROID) target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL") else () + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) + find_package(OpenGL REQUIRED) if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") endif () - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${OPENGL_LIBRARY}") - target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + # target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) endif (APPLE) diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 4ac33d8f14..b7f3ef444c 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -9,11 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Batch.h" -#include "GPUConfig.h" - -#include #include +#include + +#include "GPUConfig.h" #if defined(NSIGHT_FOUND) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 3942af4f84..4bc8abadd0 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -8,9 +8,10 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "GLBackendShared.h" + #include #include "GPULogging.h" -#include "GLBackendShared.h" #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/GLBackendQuery.cpp b/libraries/gpu/src/gpu/GLBackendQuery.cpp index 39db19dafd..297bdf9c40 100644 --- a/libraries/gpu/src/gpu/GLBackendQuery.cpp +++ b/libraries/gpu/src/gpu/GLBackendQuery.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index fd5bed2e5e..dccd035cb4 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GPULogging.h" #include "GLBackendShared.h" #include "Format.h" diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 27f58fcbe3..75bef461f9 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -11,10 +11,11 @@ #ifndef hifi_gpu_GLBackend_Shared_h #define hifi_gpu_GLBackend_Shared_h -#include "GLBackend.h" - #include +#include "GPULogging.h" +#include "GLBackend.h" + #include "Batch.h" static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 2e3149919c..5046221ad3 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,7 +36,7 @@ #else #include -#include +//#include //#include //#include diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index 392fd918a1..a1a17d29e9 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -13,13 +13,13 @@ #include #include +#include #include #include "gpu/Resource.h" #include "gpu/Texture.h" -#include namespace model { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index f19fa6e18a..22e5a705e3 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -9,15 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL -#include - -#include #include #include #include +#include #include "gpu/StandardShaderLib.h" #include "AmbientOcclusionEffect.h" @@ -176,7 +173,6 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - auto& scene = sceneContext->_scene; gpu::Batch batch; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index c14bbfcb1d..7563609026 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -12,12 +12,12 @@ #include "DeferredLightingEffect.h" #include -#include +#include +#include + #include #include #include -#include -#include #include "AbstractViewStateInterface.h" #include "GeometryCache.h" @@ -291,7 +291,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { locations = &_directionalAmbientSphereLightCascadedShadowMapLocations; } batch.setPipeline(program); - batch._glUniform3fv(locations->shadowDistances, 1, (const GLfloat*) &_viewState->getShadowDistances()); + batch._glUniform3fv(locations->shadowDistances, 1, (const float*) &_viewState->getShadowDistances()); } else { if (useSkyboxCubemap) { @@ -325,7 +325,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { sh = (*_skybox->getCubemap()->getIrradiance()); } for (int i =0; i ambientSphere + i, 1, (const GLfloat*) (&sh) + i * 4); + batch._glUniform4fv(locations->ambientSphere + i, 1, (const float*) (&sh) + i * 4); } } @@ -340,7 +340,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); } - batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); } float left, right, bottom, top, nearVal, farVal; @@ -419,9 +419,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_pointLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_pointLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_pointLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_pointLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _pointLights) { auto& light = _allocatedLights[lightID]; @@ -467,9 +467,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch._glUniform2f(_spotLightLocations.depthTexCoordOffset, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_spotLightLocations.depthTexCoordScale, depthTexCoordScaleS, depthTexCoordScaleT); - batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); + batch._glUniformMatrix4fv(_spotLightLocations.invViewMat, 1, false, reinterpret_cast< const float* >(&invViewMat)); - batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const GLfloat* >(&texcoordMat)); + batch._glUniformMatrix4fv(_spotLightLocations.texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _spotLights) { auto light = _allocatedLights[lightID]; @@ -489,7 +489,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { if ((eyeHalfPlaneDistance > -nearRadius) && (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { coneParam.w = 0.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); @@ -509,7 +509,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setViewTransform(viewMat); } else { coneParam.w = 1.0f; - batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const GLfloat* >(&coneParam)); + batch._glUniform4fv(_spotLightLocations.coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(light->getPosition()); @@ -595,9 +595,9 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); - const GLint LIGHT_GPU_SLOT = 3; + const int LIGHT_GPU_SLOT = 3; slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); - const GLint ATMOSPHERE_GPU_SLOT = 4; + const int ATMOSPHERE_GPU_SLOT = 4; slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); @@ -677,10 +677,10 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { int ringFloatOffset = slices * 3; - GLfloat* vertexData = new GLfloat[verticesSize]; - GLfloat* vertexRing0 = vertexData; - GLfloat* vertexRing1 = vertexRing0 + ringFloatOffset; - GLfloat* vertexRing2 = vertexRing1 + ringFloatOffset; + float* vertexData = new float[verticesSize]; + float* vertexRing0 = vertexData; + float* vertexRing1 = vertexRing0 + ringFloatOffset; + float* vertexRing2 = vertexRing1 + ringFloatOffset; for (int i = 0; i < slices; i++) { float theta = TWO_PI * i / slices; @@ -746,7 +746,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { *(index++) = capVertex; } - _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(GLushort) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); + _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); delete[] indexData; model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 8550f8d8b6..b873b35b9c 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -9,22 +9,22 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "GeometryCache.h" + #include #include #include #include -#include -#include - #include #include +#include +#include + #include "TextureCache.h" #include "RenderUtilsLogging.h" -#include "GeometryCache.h" #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 67eb85edfc..1e0e04c776 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -18,20 +18,18 @@ #include #include -#include -#include #include #include -#include "PhysicsEntity.h" #include #include #include +#include +#include #include "AbstractViewStateInterface.h" #include "AnimationHandle.h" #include "DeferredLightingEffect.h" #include "Model.h" -#include "RenderUtilsLogging.h" #include "model_vert.h" #include "model_shadow_vert.h" @@ -96,7 +94,7 @@ Model::~Model() { } Model::RenderPipelineLib Model::_renderPipelineLib; -const GLint MATERIAL_GPU_SLOT = 3; +const int MATERIAL_GPU_SLOT = 3; void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, gpu::ShaderPointer& vertexShader, diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index cf9b455059..b4863a4764 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -10,12 +10,10 @@ // #include "RenderDeferredTask.h" -#include -#include -#include #include #include #include +#include #include "FramebufferCache.h" #include "DeferredLightingEffect.h" diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 1a6ea97b64..d6a9bf5b36 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -16,16 +16,15 @@ #include #include -#include -#include -#include - #include #include #include #include #include +#include + + #include "RenderUtilsLogging.h" diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index f84d212112..f50f517d7d 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -15,15 +15,14 @@ #include #include +#include +#include #include #include #include #include -#include -#include - #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" #include "drawItemStatus_vert.h" @@ -171,4 +170,4 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex args->_context->syncCache(); renderContext->args->_context->syncCache(); args->_context->render((batch)); -} \ No newline at end of file +} diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0e3eba0b53..d29420fdfd 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -14,12 +14,12 @@ #include #include -#include -#include -#include #include #include #include +#include +#include +#include using namespace render; diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 3b7eb18368..ab01d333ac 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -8,30 +8,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include + #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include class RateCounter { std::vector times; From 475d069185ffe68e2979a43856d082adb747b04c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jul 2015 18:12:10 -0700 Subject: [PATCH 047/109] fix rendering on linux --- libraries/gpu/src/gpu/Config.slh | 7 +++---- libraries/gpu/src/gpu/GPUConfig.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Config.slh b/libraries/gpu/src/gpu/Config.slh index 76be161822..f24b54e5d5 100644 --- a/libraries/gpu/src/gpu/Config.slh +++ b/libraries/gpu/src/gpu/Config.slh @@ -21,10 +21,9 @@ <@def VERSION_HEADER #version 120 #extension GL_EXT_gpu_shader4 : enable@> <@else@> - <@def GPU_FEATURE_PROFILE GPU_LEGACY@> - <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120 -#extension GL_EXT_gpu_shader4 : enable@> + <@def GPU_FEATURE_PROFILE GPU_CORE@> + <@def GPU_TRANSFORM_PROFILE GPU_CORE@> + <@def VERSION_HEADER #version 430 compatibility@> <@endif@> <@endif@> diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index 5046221ad3..d9b6d18894 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -40,8 +40,8 @@ //#include //#include -#define GPU_FEATURE_PROFILE GPU_LEGACY -#define GPU_TRANSFORM_PROFILE GPU_LEGACY +#define GPU_FEATURE_PROFILE GPU_CORE +#define GPU_TRANSFORM_PROFILE GPU_CORE #endif From 7325e6a7b7fcfa280ab3ed4534f84db804402880 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 29 Jul 2015 14:57:06 +0200 Subject: [PATCH 048/109] Send ping requests to the nodes (AvatarMixer, AudioMixer, EntityServer) that the Assignment agent connected with to keep the connections alive. --- assignment-client/src/Agent.cpp | 27 ++++++++++++++++++++++++++- assignment-client/src/Agent.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index da588bc316..77b47f4d7c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -107,6 +107,7 @@ void Agent::handleAudioPacket(QSharedPointer packet) { } const QString AGENT_LOGGING_NAME = "agent"; +const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); @@ -118,6 +119,10 @@ void Agent::run() { << NodeType::EntityServer ); + _pingTimer = new QTimer(this); + connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); + _pingTimer->start(PING_INTERVAL); + // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -193,7 +198,27 @@ void Agent::run() { void Agent::aboutToFinish() { _scriptEngine.stop(); - + + _pingTimer->stop(); + delete _pingTimer; + // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); } + +void Agent::sendPingRequests() { + auto nodeList = DependencyManager::get(); + + nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { + switch (node->getType()) { + case NodeType::AvatarMixer: + case NodeType::AudioMixer: + case NodeType::EntityServer: + return true; + default: + return false; + } + }, [nodeList](const SharedNodePointer& node) { + nodeList->sendPacket(nodeList->constructPingPacket(), *node); + }); +} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 241e14439c..4c207e59aa 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,11 +58,13 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); + void sendPingRequests(); private: ScriptEngine _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; + QTimer* _pingTimer; MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; From 327ecfe62ccba1d78f5bbdb933e2382dddddb786 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Jul 2015 09:11:58 -0700 Subject: [PATCH 049/109] Be less noisy. --- interface/src/avatar/MyAvatar.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5f0b5f03da..054365b2b1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -602,7 +602,6 @@ void MyAvatar::saveData() { for (int i = 0; i < animationHandles.size(); i++) { settings.setArrayIndex(i); const AnimationHandlePointer& pointer = animationHandles.at(i); - qCDebug(interfaceapp) << "Save animation" << pointer->getURL().toString(); settings.setValue("role", pointer->getRole()); settings.setValue("url", pointer->getURL()); settings.setValue("fps", pointer->getFPS()); From 76acbde5955c1cd3f33d8e7228e19ae8899b6f34 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 09:54:10 -0700 Subject: [PATCH 050/109] Removing the opengl and glew links from Interface since now coming from gpu lib --- interface/CMakeLists.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6af3cfa08b..4f99b6b34f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -219,21 +219,22 @@ else (APPLE) $/resources ) - find_package(OpenGL REQUIRED) + # find_package(OpenGL REQUIRED) - if (${OPENGL_INCLUDE_DIR}) - include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") - endif () + # if (${OPENGL_INCLUDE_DIR}) + # include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") +# endif () - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") + # target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") # link target to external libraries if (WIN32) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) + # add_dependency_external_projects(glew) + # find_package(GLEW REQUIRED) + # target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) + # target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) + target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) if (USE_NSIGHT) # try to find the Nsight package and add it to the build if we find it From 7fdba9132e34ed987fd98faa40d8274f0235275e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Jul 2015 09:55:19 -0700 Subject: [PATCH 051/109] Revert Camera.h changes. --- interface/src/Camera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Camera.h b/interface/src/Camera.h index bddb01ef21..6eed39cf16 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -28,7 +28,7 @@ enum CameraMode }; Q_DECLARE_METATYPE(CameraMode); -// static int cameraModeId = qRegisterMetaType(); +static int cameraModeId = qRegisterMetaType(); class Camera : public QObject { Q_OBJECT From 12d449f69d92a8eb85a5e9f4966fbd7b9acd23a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Jul 2015 09:55:46 -0700 Subject: [PATCH 052/109] Update test case initialization post cauterization changes. --- tests/animation/src/RigTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index 0530ad5638..8dbba30031 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -73,7 +73,7 @@ void RigTests::initTestCase() { } _rig = std::make_shared(); - _rig->initJointStates(jointStates, glm::mat4(), geometry.neckJointIndex); + _rig->initJointStates(jointStates, glm::mat4()); std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl; } From 17650dde74598fb1cdff9df8979d7f8db096546c Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 11:49:22 -0700 Subject: [PATCH 053/109] Solving the coimpiling issue with Render-utils test --- tests/render-utils/src/main.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index ab01d333ac..7c02c7e8a7 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -19,6 +19,10 @@ #include #include #include + +#include +#include + #include #include #include @@ -29,7 +33,7 @@ #include #include #include -#include + #include @@ -90,6 +94,7 @@ class QTestWindow : public QWindow { QSize _size; //TextRenderer* _textRenderer[4]; RateCounter fps; + gpu::ContextPointer _gpuContext; protected: void renderText(); @@ -119,6 +124,9 @@ public: show(); makeCurrent(); + _gpuContext.reset(new gpu::Context(new gpu::GLBackend())); + + { QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); @@ -131,23 +139,6 @@ public: } qDebug() << (const char*)glGetString(GL_VERSION); -#ifdef WIN32 - glewExperimental = true; - GLenum err = glewInit(); - if (GLEW_OK != err) { - /* Problem: glewInit failed, something is seriously wrong. */ - const GLubyte * errStr = glewGetErrorString(err); - qDebug("Error: %s\n", errStr); - } - qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } - glGetError(); -#endif - //_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false); //_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false, // TextRenderer::SHADOW_EFFECT); From 661f29924fd79f6be76468ff5f6376b8153db557 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 13:55:26 -0700 Subject: [PATCH 054/109] Clean up the cmakelist to normally onlly do th eminimal linking and include for gl --- interface/CMakeLists.txt | 29 ++++------------------------- libraries/gpu/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4f99b6b34f..b6c8e3f3f4 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -218,40 +218,19 @@ else (APPLE) "${PROJECT_SOURCE_DIR}/resources" $/resources ) - - # find_package(OpenGL REQUIRED) - - # if (${OPENGL_INCLUDE_DIR}) - # include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") -# endif () - - # target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - + # link target to external libraries if (WIN32) - # add_dependency_external_projects(glew) - # find_package(GLEW REQUIRED) - # target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - - # target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) - + if (USE_NSIGHT) - # try to find the Nsight package and add it to the build if we find it - find_package(NSIGHT) + # If required NSIGHT lib comes from gpu lib but still to add a precroc define if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) add_definitions(-DNSIGHT_FOUND) - target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() - else (WIN32) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) - - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES}) - message(STATUS ${GLEW_LIBRARY}) + # Nothing else required on linux apparently endif() endif (APPLE) diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index eda168091e..1e8faf1bca 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -23,7 +23,7 @@ elseif (WIN32) # try to find the Nsight package and add it to the build if we find it find_package(NSIGHT) if (NSIGHT_FOUND) - include_directories(${NSIGHT_INCLUDE_DIRS}) + target_include_directories(${TARGET_NAME} PUBLIC ${NSIGHT_INCLUDE_DIRS}) add_definitions(-DNSIGHT_FOUND) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () From 9601e09ba9301005057a608893e82e23f7734427 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 14:42:24 -0700 Subject: [PATCH 055/109] A simpler way to add the NSIGHT_FOUND define to all the projects depending on GPU --- interface/CMakeLists.txt | 8 +------- libraries/gpu/CMakeLists.txt | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d47395a810..f1ef38ade9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -221,14 +221,8 @@ else (APPLE) # link target to external libraries if (WIN32) + # target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) - - if (USE_NSIGHT) - # If required NSIGHT lib comes from gpu lib but still to add a precroc define - if (NSIGHT_FOUND) - add_definitions(-DNSIGHT_FOUND) - endif () - endif() else (WIN32) # Nothing else required on linux apparently endif() diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 1e8faf1bca..89939535b0 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -21,10 +21,11 @@ elseif (WIN32) if (USE_NSIGHT) # try to find the Nsight package and add it to the build if we find it + # note that this will also enable NSIGHT profilers in all the projects linking gpu find_package(NSIGHT) if (NSIGHT_FOUND) target_include_directories(${TARGET_NAME} PUBLIC ${NSIGHT_INCLUDE_DIRS}) - add_definitions(-DNSIGHT_FOUND) + target_compile_definitions(${TARGET_NAME} PUBLIC NSIGHT_FOUND) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() @@ -39,8 +40,7 @@ else () if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") endif () - + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" "${OPENGL_LIBRARY}") - - # target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + endif (APPLE) From 3c934af297617fcceb90b5b70366c673be5dc768 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 14:45:48 -0700 Subject: [PATCH 056/109] clean the gpuCOnfig.h for linux --- libraries/gpu/src/gpu/GPUConfig.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gpu/src/gpu/GPUConfig.h index d9b6d18894..5590c4cc8d 100644 --- a/libraries/gpu/src/gpu/GPUConfig.h +++ b/libraries/gpu/src/gpu/GPUConfig.h @@ -36,9 +36,6 @@ #else #include -//#include -//#include -//#include #define GPU_FEATURE_PROFILE GPU_CORE #define GPU_TRANSFORM_PROFILE GPU_CORE From 269db0ff6fc71aeca8179e173ad3e9e042f1da6c Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:08:16 -0700 Subject: [PATCH 057/109] fixing the stars rendering that was vilently broken durign the hunt for GPUCOnfig.h includes --- interface/src/Stars.cpp | 6 +----- libraries/gpu/src/gpu/GLBackendState.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 7b612acb68..68e0001996 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -141,6 +141,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); + state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _gridPipeline.reset(gpu::Pipeline::create(program, state)); } @@ -205,8 +206,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch._glUniform1f(_timeSlot, secs); geometryCache->renderUnitCube(batch); - //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - static const size_t VERTEX_STRIDE = sizeof(StarVertex); size_t offset = offsetof(StarVertex, position); gpu::BufferView posView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement); @@ -215,9 +214,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { // Render the stars batch.setPipeline(_starsPipeline); - //batch._glEnable(GL_PROGRAM_POINT_SIZE_EXT); - //batch._glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - //batch._glEnable(GL_POINT_SMOOTH); batch.setInputFormat(streamFormat); batch.setInputBuffer(VERTICES_SLOT, posView); diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index 18fc9ddd3c..e9dcd3aad3 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -482,6 +482,12 @@ void GLBackend::syncPipelineStateCache() { State::Data state; glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + // Point size is always on + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + getCurrentGLState(state); State::Signature signature = State::evalSignature(state); From 5baf993c24a70b26de159259a5a6fbb5d6ffbc0f Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:27:45 -0700 Subject: [PATCH 058/109] fixing the stars again --- interface/src/Stars.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 68e0001996..42b1a3f2e2 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -141,7 +141,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); - state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _gridPipeline.reset(gpu::Pipeline::create(program, state)); } @@ -153,6 +152,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto state = gpu::StatePointer(new gpu::State()); // enable decal blend state->setDepthTest(gpu::State::DepthTest(false)); + state->setAntialiasedLineEnable(true); // line smoothing also smooth points state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _starsPipeline.reset(gpu::Pipeline::create(program, state)); @@ -219,6 +219,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { batch.setInputBuffer(VERTICES_SLOT, posView); batch.setInputBuffer(COLOR_SLOT, colView); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); - + renderArgs->_context->render(batch); } From 4972cb024f34eb5fe2f4cfa34080be52ae06f3b2 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 29 Jul 2015 16:48:23 -0700 Subject: [PATCH 059/109] Try to make the inlucde sequence simpler in gpu for GLBackend --- libraries/gpu/src/gpu/Batch.cpp | 18 ------------------ libraries/gpu/src/gpu/GLBackend.cpp | 1 - libraries/gpu/src/gpu/GLBackend.h | 2 -- libraries/gpu/src/gpu/GLBackendShared.h | 2 -- 4 files changed, 23 deletions(-) diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index b7f3ef444c..a10f5b3de7 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -10,12 +10,6 @@ // #include "Batch.h" -#include -#include - -#include "GPUConfig.h" - - #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" @@ -288,15 +282,3 @@ void Batch::getQuery(const QueryPointer& query) { _params.push_back(_queries.cache(query)); } -void push_back(Batch::Params& params, const vec3& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); -} - -void push_back(Batch::Params& params, const vec4& v) { - params.push_back(v.x); - params.push_back(v.y); - params.push_back(v.z); - params.push_back(v.a); -} diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 4bc8abadd0..51a767882c 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -11,7 +11,6 @@ #include "GLBackendShared.h" #include -#include "GPULogging.h" #include using namespace gpu; diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 9d8c9ef805..3b1d4cde5e 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -18,8 +18,6 @@ #include "GPUConfig.h" #include "Context.h" -#include "Batch.h" - namespace gpu { diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 75bef461f9..888fd1164d 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -16,8 +16,6 @@ #include "GPULogging.h" #include "GLBackend.h" -#include "Batch.h" - static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_POINTS, GL_LINES, From c2934bdb5d3fc8e1b1fac2d60c0eec1430ae8d06 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 29 Jul 2015 17:35:29 -0700 Subject: [PATCH 060/109] Fix avatar head, eye and torso twist. * Updated SkeletionModel::updateRig to explicitly pass a set of HeadParameters to the rig to do procedural animation. * Moved torso lean procedural animation from SkeletonModel into Rig. * Moved eye tracking procedural animation from HeadModel into Rig. * Moved neck procedural animation from HeadModel into Rig. --- interface/src/avatar/FaceModel.cpp | 54 --------------------- interface/src/avatar/FaceModel.h | 10 ++-- interface/src/avatar/SkeletonModel.cpp | 57 +++++++--------------- libraries/animation/src/AvatarRig.cpp | 18 ------- libraries/animation/src/AvatarRig.h | 1 - libraries/animation/src/EntityRig.h | 1 - libraries/animation/src/Rig.cpp | 65 ++++++++++++++++++++++++++ libraries/animation/src/Rig.h | 25 ++++++++-- 8 files changed, 108 insertions(+), 123 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index e35c70a8ab..d5d4da8665 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -50,60 +50,6 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } } -void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { - // get the rotation axes in joint space and use them to adjust the rotation - glm::mat3 axes = glm::mat3_cast(glm::quat()); - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * - glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation))); - glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); - glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), - _owningHead->getTorsoTwist(), - _owningHead->getFinalLeanSideways())); - pitchYawRoll -= lean; - _rig->setJointRotationInConstrainedFrame(index, - glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) - * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) - * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) - * joint.rotation, DEFAULT_PRIORITY); -} - -void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) { - // likewise with the eye joints - // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. - glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * - glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * - joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); - glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); - glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation(); - glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f)); - glm::quat between = rotationBetween(front, lookAt); - const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), - -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation, DEFAULT_PRIORITY); -} - -void FaceModel::updateJointState(int index) { - const JointState& state = _rig->getJointState(index); - const FBXJoint& joint = state.getFBXJoint(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { - const JointState& parentState = _rig->getJointState(joint.parentIndex); - if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint, index); - - } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(this, parentState, joint, index); - } - } - - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->updateFaceJointState(index, parentTransform); -} - bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index ce78c51e70..5a19a8ea29 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -19,23 +19,19 @@ class Head; /// A face formed from a linear mix of blendshapes according to a set of coefficients. class FaceModel : public Model { Q_OBJECT - + public: FaceModel(Head* owningHead, RigPointer rig); virtual void simulate(float deltaTime, bool fullUpdate = true); - - virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); - virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); - virtual void updateJointState(int index); /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - + private: - + Head* _owningHead; }; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 122559bedb..a766a80bab 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -118,6 +118,24 @@ void SkeletonModel::updateClusterMatrices() { void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); Model::updateRig(deltaTime, parentTransform); + if (_owningAvatar->isMyAvatar()) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + + Rig::HeadParameters params; + params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways(); + params.leanForward = _owningAvatar->getHead()->getFinalLeanSideways(); + params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist(); + params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame(); + params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame(); + params.eyeLookAt = _owningAvatar->getHead()->getCorrectedLookAtPosition(); + params.eyeSaccade = _owningAvatar->getHead()->getSaccade(); + params.leanJointIndex = geometry.leanJointIndex; + params.neckJointIndex = geometry.neckJointIndex; + params.leftEyeJointIndex = geometry.leftEyeJointIndex; + params.rightEyeJointIndex = geometry.rightEyeJointIndex; + + _rig->updateFromHeadParameters(params); + } } void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { @@ -259,51 +277,12 @@ void SkeletonModel::updateJointState(int index) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - const JointState joint = _rig->getJointState(index); - if (joint.getParentIndex() >= 0 && joint.getParentIndex() < _rig->getJointStateCount()) { - const JointState parentState = _rig->getJointState(joint.getParentIndex()); - if (index == geometry.leanJointIndex) { - maybeUpdateLeanRotation(parentState, index); - - } else if (index == geometry.neckJointIndex) { - maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index); - - } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { - maybeUpdateEyeRotation(parentState, joint.getFBXJoint(), index); - } - } - _rig->updateJointState(index, parentTransform); if (index == _geometry->getFBXGeometry().rootJointIndex) { _rig->clearJointTransformTranslation(index); } } - -void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, int index) { - if (!_owningAvatar->isMyAvatar()) { - return; - } - // get the rotation axes in joint space and use them to adjust the rotation - glm::vec3 xAxis(1.0f, 0.0f, 0.0f); - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - glm::quat inverse = glm::inverse(parentState.getRotation() * _rig->getJointDefaultRotationInParentFrame(index)); - _rig->setJointRotationInConstrainedFrame(index, - glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis) - * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis) - * glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis) - * _rig->getJointState(index).getFBXJoint().rotation, LEAN_PRIORITY); -} - -void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index); -} - -void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index) { - _owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, index); -} - void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index cf05e61cdb..3b48b0814f 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -31,21 +31,3 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { } } } - - -void AvatarRig::updateFaceJointState(int index, glm::mat4 parentTransform) { - JointState& state = _jointStates[index]; - const FBXJoint& joint = state.getFBXJoint(); - - // compute model transforms - int parentIndex = joint.parentIndex; - if (parentIndex == -1) { - state.computeTransform(parentTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h index dbffd8aa45..4a111a535b 100644 --- a/libraries/animation/src/AvatarRig.h +++ b/libraries/animation/src/AvatarRig.h @@ -22,7 +22,6 @@ class AvatarRig : public Rig { public: ~AvatarRig() {} virtual void updateJointState(int index, glm::mat4 parentTransform); - virtual void updateFaceJointState(int index, glm::mat4 parentTransform); }; #endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h index aa6a5fbd2b..e8e15a5a28 100644 --- a/libraries/animation/src/EntityRig.h +++ b/libraries/animation/src/EntityRig.h @@ -22,7 +22,6 @@ class EntityRig : public Rig { public: ~EntityRig() {} virtual void updateJointState(int index, glm::mat4 parentTransform); - virtual void updateFaceJointState(int index, glm::mat4 parentTransform) { } }; #endif // hifi_EntityRig_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index dc7b37129e..2ff6faa868 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -665,3 +665,68 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { } return _jointStates[jointIndex].getDefaultRotationInParentFrame(); } + +void Rig::updateFromHeadParameters(const HeadParameters& params) { + updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist); + updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist); + updateEyeJoint(params.leftEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.rightEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); +} + +void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { + if (index > 0 && _jointStates[index].getParentIndex() > 0) { + auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; + + // get the rotation axes in joint space and use them to adjust the rotation + glm::vec3 xAxis(1.0f, 0.0f, 0.0f); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index)); + setJointRotationInConstrainedFrame(index, + glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) * + glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) * + glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) * + getJointState(index).getFBXJoint().rotation, DEFAULT_PRIORITY); + } +} + +void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) { + if (index > 0 && _jointStates[index].getParentIndex() > 0) { + auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; + auto joint = _jointStates[index].getFBXJoint(); + + // get the rotation axes in joint space and use them to adjust the rotation + glm::mat3 axes = glm::mat3_cast(glm::quat()); + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * + glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation))); + glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation); + glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways)); + pitchYawRoll -= lean; + setJointRotationInConstrainedFrame(index, + glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) * + glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) * + glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) * + joint.rotation, DEFAULT_PRIORITY); + } +} + +void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) { + if ( index > 0 && _jointStates[index].getParentIndex() > 0) { + auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; + auto joint = _jointStates[index].getFBXJoint(); + + // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. + glm::mat4 inverse = glm::inverse(parentState.getTransform() * + glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f)); + glm::vec3 lookAtDelta = lookAt; + glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f)); + glm::quat between = rotationBetween(front, lookAt); + const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; + float angle = glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE); + glm::quat rot = glm::angleAxis(angle, glm::axis(between)); + setJointRotationInConstrainedFrame(index, rot * joint.rotation, DEFAULT_PRIORITY); + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index fe6bc82f35..2eb9d0e0b3 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -47,14 +47,27 @@ typedef std::shared_ptr AnimationHandlePointer; class Rig; typedef std::shared_ptr RigPointer; - class Rig : public QObject, public std::enable_shared_from_this { public: + struct HeadParameters { + float leanSideways = 0.0f; // degrees + float leanForward = 0.0f; // degrees + float torsoTwist = 0.0f; // degrees + glm::quat localHeadOrientation = glm::quat(); + glm::quat worldHeadOrientation = glm::quat(); + glm::vec3 eyeLookAt = glm::vec3(); // world space + glm::vec3 eyeSaccade = glm::vec3(); // world space + int leanJointIndex = -1; + int neckJointIndex = -1; + int leftEyeJointIndex = -1; + int rightEyeJointIndex = -1; + }; + virtual ~Rig() {} - RigPointer getRigPointer() { return shared_from_this(); } + RigPointer getRigPointer() { return shared_from_this(); } AnimationHandlePointer createAnimationHandle(); void removeAnimationHandle(const AnimationHandlePointer& handle); @@ -129,11 +142,17 @@ public: void updateVisibleJointStates(); virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; - virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0; void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } + void updateFromHeadParameters(const HeadParameters& params); + protected: + + void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); + void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist); + void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + QVector _jointStates; QList _animationHandles; From dc34c025bd1b6413ed5306f4d4f5cff4ce0f686b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 29 Jul 2015 18:54:05 -0700 Subject: [PATCH 061/109] whitespace --- libraries/animation/src/Rig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2eb9d0e0b3..70f0a59a21 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -67,7 +67,7 @@ public: virtual ~Rig() {} - RigPointer getRigPointer() { return shared_from_this(); } + RigPointer getRigPointer() { return shared_from_this(); } AnimationHandlePointer createAnimationHandle(); void removeAnimationHandle(const AnimationHandlePointer& handle); From a9556660c4ee2995eb5f96039c7489a0bf276d8b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 29 Jul 2015 20:53:24 -0700 Subject: [PATCH 062/109] fix linux build --- libraries/entities/src/ParticleEffectEntityItem.cpp | 6 +++--- libraries/gpu/src/gpu/Batch.cpp | 2 ++ libraries/gpu/src/gpu/GLBackendShader.cpp | 3 +-- libraries/gpu/src/gpu/GLBackendState.cpp | 10 +++++----- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 4dfc9dd436..dc5bbb85ed 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -155,9 +155,9 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { 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))); + glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)), + 2.0f * std::max(fabsf(yMin), fabsf(yMax)), + 2.0f * std::max(fabsf(zMin), fabsf(zMax))); EntityItem::setDimensions(dims); } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index a10f5b3de7..0347536808 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -8,6 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "Batch.h" #if defined(NSIGHT_FOUND) diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index dccd035cb4..5fa0e277e5 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -639,14 +639,13 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin GLchar name[NAME_LENGTH]; GLint length = 0; GLint size = 0; - GLenum type = 0; GLint binding = -1; glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - + GLuint blockIndex = glGetUniformBlockIndex(glprogram, name); // CHeck if there is a requested binding for this block diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index e9dcd3aad3..22c61b2365 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -482,15 +482,15 @@ void GLBackend::syncPipelineStateCache() { State::Data state; glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - - // Point size is always on - glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - glEnable(GL_PROGRAM_POINT_SIZE_EXT); + + // Point size is always on + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); getCurrentGLState(state); State::Signature signature = State::evalSignature(state); - + _pipeline._stateCache = state; _pipeline._stateSignatureCache = signature; } From 4bb415fd0d6ffe8b5e936298bfa0d9359549ed67 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 09:29:59 -0700 Subject: [PATCH 063/109] Fix for torso twist. Off by one error, 0 is a valid parent bone index. --- libraries/animation/src/Rig.cpp | 25 ++++++++++++++++++++++--- libraries/animation/src/Rig.h | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2ff6faa868..15f3dd65ec 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -16,6 +16,25 @@ #include "AnimationLogging.h" #include "Rig.h" +void Rig::HeadParameters::dump() const { + qCDebug(animation, "HeadParameters ="); + qCDebug(animation, " leanSideways = %0.5f", leanSideways); + qCDebug(animation, " leanForward = %0.5f", leanForward); + qCDebug(animation, " torsoTwist = %0.5f", torsoTwist); + glm::vec3 axis = glm::axis(localHeadOrientation); + float theta = glm::angle(localHeadOrientation); + qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + axis = glm::axis(worldHeadOrientation); + theta = glm::angle(worldHeadOrientation); + qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta); + qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z); + qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z); + qCDebug(animation, " leanJointIndex = %.d", leanJointIndex); + qCDebug(animation, " neckJointIndex = %.d", neckJointIndex); + qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex); + qCDebug(animation, " rightEyeJointIndex = %.d", rightEyeJointIndex); +} + void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { @@ -674,7 +693,7 @@ void Rig::updateFromHeadParameters(const HeadParameters& params) { } void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { - if (index > 0 && _jointStates[index].getParentIndex() > 0) { + if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; // get the rotation axes in joint space and use them to adjust the rotation @@ -691,7 +710,7 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa } void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) { - if (index > 0 && _jointStates[index].getParentIndex() > 0) { + if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; auto joint = _jointStates[index].getFBXJoint(); @@ -712,7 +731,7 @@ void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, floa } void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) { - if ( index > 0 && _jointStates[index].getParentIndex() > 0) { + if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; auto joint = _jointStates[index].getFBXJoint(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 70f0a59a21..2af61bfded 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -63,6 +63,8 @@ public: int neckJointIndex = -1; int leftEyeJointIndex = -1; int rightEyeJointIndex = -1; + + void dump() const; }; virtual ~Rig() {} From fc80e427b9ceef1f61acf4f6005ef1a688091f94 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 30 Jul 2015 11:08:45 -0700 Subject: [PATCH 064/109] remove names from audio-mixer jitter calc comments --- assignment-client/src/audio/AudioMixer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b68332210b..04cbb0b325 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -927,9 +927,9 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc"; _streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool(); if (_streamSettings._useStDevForJitterCalc) { - qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled"; + qDebug() << "Using stdev method for jitter calc if dynamic jitter buffers enabled"; } else { - qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled"; + qDebug() << "Using max-gap method for jitter calc if dynamic jitter buffers enabled"; } const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold"; From 314434e0689098d567f76e2cb2c92b586e9c5dc3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 12:27:28 -0700 Subject: [PATCH 065/109] Re-added neck and eye procedural animation to FaceModel. This is only used in the case of split head/body avatars. --- interface/src/avatar/FaceModel.cpp | 61 +++++++++++++++++++++++++++++- interface/src/avatar/FaceModel.h | 4 ++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index d5d4da8665..f909489dc9 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -25,6 +25,7 @@ FaceModel::FaceModel(Head* owningHead, RigPointer rig) : void FaceModel::simulate(float deltaTime, bool fullUpdate) { updateGeometry(); + Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); glm::vec3 neckPosition; if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { @@ -37,19 +38,75 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } setRotation(neckParentRotation); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale()); - + setPupilDilation(_owningHead->getPupilDilation()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); - + // FIXME - this is very expensive, we shouldn't do it if we don't have to //invalidCalculatedMeshBoxes(); if (isActive()) { setOffset(-_geometry->getFBXGeometry().neckPivot); + + for (int i = 0; i < _rig->getJointStateCount(); i++) { + updateJointState(i); + } + Model::simulateInternal(deltaTime); } } +void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) { + // get the rotation axes in joint space and use them to adjust the rotation + glm::mat3 axes = glm::mat3_cast(glm::quat()); + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation))); + glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); + glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), + _owningHead->getTorsoTwist(), + _owningHead->getFinalLeanSideways())); + pitchYawRoll -= lean; + _rig->setJointRotationInConstrainedFrame(index, + glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) + * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) + * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) + * joint.rotation, DEFAULT_PRIORITY); +} + +void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) { + // likewise with the eye joints + // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. + glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() * + glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) * + joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); + glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); + glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation(); + glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f)); + glm::quat between = rotationBetween(front, lookAt); + const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; + _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between), + -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation, DEFAULT_PRIORITY); +} + +void FaceModel::updateJointState(int index) { + const JointState& state = _rig->getJointState(index); + const FBXJoint& joint = state.getFBXJoint(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + + // guard against out-of-bounds access to _jointStates + if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) { + const JointState& parentState = _rig->getJointState(joint.parentIndex); + if (index == geometry.neckJointIndex) { + maybeUpdateNeckRotation(parentState, joint, index); + + } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) { + maybeUpdateEyeRotation(this, parentState, joint, index); + } + } +} + bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 5a19a8ea29..40f502f2d3 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -26,6 +26,10 @@ public: virtual void simulate(float deltaTime, bool fullUpdate = true); + void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); + void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); + void updateJointState(int index); + /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; From 7a10b31dd94f999aa1ca571a6886c0e2c5beed22 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 15:04:27 -0700 Subject: [PATCH 066/109] Clear translation on root joint. Also, delete/rename all instances of updateJointState except for the one in Rig and derived classes. --- interface/src/avatar/FaceModel.cpp | 4 ++-- interface/src/avatar/FaceModel.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 33 -------------------------- interface/src/avatar/SkeletonModel.h | 13 ---------- libraries/animation/src/AvatarRig.cpp | 1 + libraries/render-utils/src/Model.cpp | 8 +------ libraries/render-utils/src/Model.h | 3 --- 7 files changed, 5 insertions(+), 59 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index f909489dc9..066144a425 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -49,7 +49,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { setOffset(-_geometry->getFBXGeometry().neckPivot); for (int i = 0; i < _rig->getJointStateCount(); i++) { - updateJointState(i); + maybeUpdateNeckAndEyeRotation(i); } Model::simulateInternal(deltaTime); @@ -90,7 +90,7 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta joint.rotation, DEFAULT_PRIORITY); } -void FaceModel::updateJointState(int index) { +void FaceModel::maybeUpdateNeckAndEyeRotation(int index) { const JointState& state = _rig->getJointState(index); const FBXJoint& joint = state.getFBXJoint(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index 40f502f2d3..54d685c73c 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -28,7 +28,7 @@ public: void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index); - void updateJointState(int index); + void maybeUpdateNeckAndEyeRotation(int index); /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 3830360c0e..1b3298c75d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -83,29 +83,6 @@ void SkeletonModel::initJointStates(QVector states) { const float PALM_PRIORITY = DEFAULT_PRIORITY; const float LEAN_PRIORITY = DEFAULT_PRIORITY; - -void SkeletonModel::updateClusterMatrices() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelToWorld = glm::mat4_cast(_rotation); - for (int i = 0; i < _meshStates.size(); i++) { - MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); - if (_showTrueJointTransforms) { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix; - } - } else { - for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); - state.clusterMatrices[j] = - modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix; - } - } - } -} - void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); Model::updateRig(deltaTime, parentTransform); @@ -264,16 +241,6 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { } } -void SkeletonModel::updateJointState(int index) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - - _rig->updateJointState(index, parentTransform); - - if (index == _geometry->getFBXGeometry().rootJointIndex) { - _rig->clearJointTransformTranslation(index); - } -} void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index a089c7b6ef..ecc5c80118 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -117,19 +117,6 @@ protected: void applyHandPosition(int jointIndex, const glm::vec3& position); void applyPalmData(int jointIndex, PalmData& palm); - - /// Updates the state of the joint at the specified index. - virtual void updateJointState(int index); - - void maybeUpdateLeanRotation(const JointState& parentState, int index); - void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index); - void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index); - - void updateClusterMatrices(); - void cauterizeHead(); - void initHeadBones(); - void invalidateHeadBones(); - private: void renderJointConstraints(gpu::Batch& batch, int jointIndex); diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 3b48b0814f..919ea43e7d 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -23,6 +23,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { int parentIndex = joint.parentIndex; if (parentIndex == -1) { state.computeTransform(parentTransform); + clearJointTransformTranslation(index); } else { // guard against out-of-bounds access to _jointStates if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ec423f7091..72bc818b70 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1375,7 +1375,7 @@ void Model::simulateInternal(float deltaTime) { } } } - + // post the blender if we're not currently waiting for one to finish if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; @@ -1383,12 +1383,6 @@ void Model::simulateInternal(float deltaTime) { } } -void Model::updateJointState(int index) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->updateJointState(index, parentTransform); -} - bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 449cd42a25..09504c7501 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -276,9 +276,6 @@ protected: void simulateInternal(float deltaTime); virtual void updateRig(float deltaTime, glm::mat4 parentTransform); - /// Updates the state of the joint at the specified index. - virtual void updateJointState(int index); - /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame From 14f4c9c6c04b462f177828717803f52256902887 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 30 Jul 2015 15:07:36 -0700 Subject: [PATCH 067/109] REmove more of the unnecessary GLBacken .h and GPUCOnfig.h include, The gpu::Context is now completely agnostic of the True Backend --- interface/src/Application.cpp | 5 ++-- interface/src/GLCanvas.h | 1 - libraries/gpu/src/gpu/Context.cpp | 17 ++++++----- libraries/gpu/src/gpu/Context.h | 28 +++++++++++++++---- libraries/gpu/src/gpu/GLBackend.cpp | 18 ++++++++---- libraries/gpu/src/gpu/GLBackend.h | 10 +++++-- .../render-utils/src/FramebufferCache.cpp | 1 - libraries/render/src/render/DrawStatus.cpp | 13 ++++----- libraries/render/src/render/DrawTask.cpp | 2 -- tests/render-utils/src/main.cpp | 4 +-- 10 files changed, 63 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1c91f9282c..047596e40a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -772,8 +772,9 @@ void Application::initializeGL() { } #endif - // Where the gpuContext is created and where the TRUE Backend is created and assigned - _gpuContext = std::make_shared(new gpu::GLBackend()); + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + gpu::Context::init(); + _gpuContext = std::make_shared(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index 925061edd0..6c53a17e04 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -13,7 +13,6 @@ #define hifi_GLCanvas_h #include -#include #include #include diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 6730be33bb..239c460c77 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -10,13 +10,16 @@ // #include "Context.h" -// this include should disappear! as soon as the gpu::Context is in place -#include "GLBackend.h" - using namespace gpu; -Context::Context(Backend* backend) : - _backend(backend) { +Context::CreateBackend Context::_createBackendCallback = nullptr; +Context::MakeProgram Context::_makeProgramCallback = nullptr; +std::once_flag Context::_initialized; + +Context::Context() { + if (_createBackendCallback) { + _backend.reset(_createBackendCallback()); + } } Context::Context(const Context& context) { @@ -26,8 +29,8 @@ Context::~Context() { } bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { - if (shader.isProgram()) { - return GLBackend::makeProgram(shader, bindings); + if (shader.isProgram() && _makeProgramCallback) { + return _makeProgramCallback(shader, bindings); } return false; } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index ab7a1d1c11..7158bd1a6d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -12,6 +12,7 @@ #define hifi_gpu_Context_h #include +#include #include "Batch.h" @@ -26,13 +27,12 @@ namespace gpu { class Backend { public: - virtual~ Backend() {}; + virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; - class TransformObject { public: Mat4 _model; @@ -118,7 +118,21 @@ protected: class Context { public: - Context(Backend* backend); + typedef Backend* (*CreateBackend)(); + typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings); + + + // This one call must happen before any context is created or used (Shader::MakeProgram) in order to setup the Backend and any singleton data needed + template + static void init() { + std::call_once(_initialized, [] { + _createBackendCallback = T::createBackend; + _makeProgramCallback = T::makeProgram; + T::init(); + }); + } + + Context(); ~Context(); void render(Batch& batch); @@ -132,13 +146,17 @@ public: protected: Context(const Context& context); + std::unique_ptr _backend; + // This function can only be called by "static Shader::makeProgram()" // makeProgramShader(...) make a program shader ready to be used in a Batch. // It compiles the sub shaders, link them and defines the Slots and their bindings. // If the shader passed is not a program, nothing happens. - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); - std::unique_ptr _backend; + static CreateBackend _createBackendCallback; + static MakeProgram _makeProgramCallback; + static std::once_flag _initialized; friend class Shader; }; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 198fe4802a..c74a94e03e 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -63,12 +63,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glLineWidth), }; -GLBackend::GLBackend() : - _input(), - _transform(), - _pipeline(), - _output() -{ +void GLBackend::init() { static std::once_flag once; std::call_once(once, [] { qCDebug(gpulogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); @@ -108,7 +103,18 @@ GLBackend::GLBackend() : }*/ #endif }); +} +Backend* GLBackend::createBackend() { + return new GLBackend(); +} + +GLBackend::GLBackend() : + _input(), + _transform(), + _pipeline(), + _output() +{ initInput(); initTransform(); } diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 3686c5138d..e86424ceb7 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -22,10 +22,17 @@ namespace gpu { class GLBackend : public Backend { -public: + + // Context Backend static interface required + friend class Context; + static void init(); + static Backend* createBackend(); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); explicit GLBackend(bool syncCache); GLBackend(); +public: + virtual ~GLBackend(); virtual void render(Batch& batch); @@ -47,7 +54,6 @@ public: static void checkGLStackStable(std::function f); - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); class GLBuffer : public GPUObject { diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 601d99108d..d6ebd001d2 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "RenderUtilsLogging.h" static QQueue _cachedFramebuffers; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index f50f517d7d..cf3616a83a 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -18,10 +18,7 @@ #include #include -#include #include -#include -#include #include "drawItemBounds_vert.h" #include "drawItemBounds_frag.h" @@ -151,17 +148,17 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex const unsigned int VEC3_ADRESS_OFFSET = 3; for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); batch.draw(gpu::LINES, 24, 0); } batch.setPipeline(getDrawItemStatusPipeline()); for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i)); - batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i)); + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); batch.draw(gpu::TRIANGLES, 24, 0); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 1a3b68af03..36ff302952 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -17,9 +17,7 @@ #include #include #include -#include #include -#include using namespace render; diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 7c02c7e8a7..9abd533650 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -94,7 +94,6 @@ class QTestWindow : public QWindow { QSize _size; //TextRenderer* _textRenderer[4]; RateCounter fps; - gpu::ContextPointer _gpuContext; protected: void renderText(); @@ -124,7 +123,8 @@ public: show(); makeCurrent(); - _gpuContext.reset(new gpu::Context(new gpu::GLBackend())); + + gpu::Context::init(); From e52bf2e12de0c1bd6f916a5df7606f3fda2c662f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 30 Jul 2015 15:51:34 -0700 Subject: [PATCH 068/109] Fix for head cauterization. When rendering rigidly bound mesh clusters were not properly using the cauterization matrix set. --- libraries/render-utils/src/Model.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 72bc818b70..417a4bb16f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1625,15 +1625,21 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (isSkinned) { - const float* bones = (const float*)state.clusterMatrices.constData(); + const float* bones; if (_cauterizeBones) { bones = (const float*)state.cauterizedClusterMatrices.constData(); + } else { + bones = (const float*)state.clusterMatrices.constData(); } batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, bones); _transforms[0] = Transform(); _transforms[0].preTranslate(_translation); } else { - _transforms[0] = Transform(state.clusterMatrices[0]); + if (_cauterizeBones) { + _transforms[0] = Transform(state.cauterizedClusterMatrices[0]); + } else { + _transforms[0] = Transform(state.clusterMatrices[0]); + } _transforms[0].preTranslate(_translation); } batch.setModelTransform(_transforms[0]); From 78a900c8661a8565557af37f0726ddfbed76011a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 30 Jul 2015 17:01:48 -0700 Subject: [PATCH 069/109] Prototype blend. Just equal weighting for now. --- libraries/animation/src/AnimationHandle.cpp | 4 +++- libraries/animation/src/AnimationHandle.h | 2 ++ libraries/animation/src/JointState.cpp | 5 +++-- libraries/animation/src/JointState.h | 2 +- libraries/animation/src/Rig.cpp | 7 +++++-- libraries/animation/src/Rig.h | 2 +- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index e8ac6e0a10..605fb25f1c 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -176,7 +176,9 @@ void AnimationHandle::applyFrame(float frameIndex) { safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), - _priority); + _priority, + false, + _mix); } } } diff --git a/libraries/animation/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h index 5d39682a0a..42e564944e 100644 --- a/libraries/animation/src/AnimationHandle.h +++ b/libraries/animation/src/AnimationHandle.h @@ -63,6 +63,7 @@ public: void setPriority(float priority); float getPriority() const { return _priority; } + void setMix(float mix) { _mix = mix; } void setMaskedJoints(const QStringList& maskedJoints); const QStringList& getMaskedJoints() const { return _maskedJoints; } @@ -119,6 +120,7 @@ private: QString _role; QUrl _url; float _priority; + float _mix; QStringList _maskedJoints; QVector _jointMappings; diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 8f14342e80..3682837719 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -232,12 +232,13 @@ glm::quat JointState::computeVisibleParentRotation() const { return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation); } -void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) { +void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) { if (priority >= _animationPriority || _animationPriority == 0.0f) { if (constrain && _constraint) { _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f); } - setRotationInConstrainedFrameInternal(targetRotation); + auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix); + setRotationInConstrainedFrameInternal(rotation); _animationPriority = priority; } } diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index f15590e5f2..93bf83d2c4 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -84,7 +84,7 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false); - void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false); + void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 15f3dd65ec..8406b61d12 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -390,7 +390,10 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { + int nAnimationsSoFar = 0; foreach (const AnimationHandlePointer& handle, _runningAnimations) { + handle->setMix(1.0f / ++nAnimationsSoFar); + handle->setPriority(1.0); handle->simulate(deltaTime); } @@ -640,13 +643,13 @@ glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); } -glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) { +glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain, float mix) { glm::quat endRotation; if (jointIndex == -1 || _jointStates.isEmpty()) { return endRotation; } JointState& state = _jointStates[jointIndex]; - state.setRotationInConstrainedFrame(targetRotation, priority, constrain); + state.setRotationInConstrainedFrame(targetRotation, priority, constrain, mix); endRotation = state.getRotationInConstrainedFrame(); return endRotation; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2af61bfded..52db16826a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -139,7 +139,7 @@ public: glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false); glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, - float priority, bool constrain = false); + float priority, bool constrain = false, float mix = 1.0f); glm::quat getJointDefaultRotationInParentFrame(int jointIndex); void updateVisibleJointStates(); From fbf21cb089a4579b507dee31110266f183429243 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 30 Jul 2015 18:27:47 -0700 Subject: [PATCH 070/109] FIxed the problem on Mac, by removing all of the gpuConfig includesgit status q :q wq --- libraries/gpu/src/gpu/GLBackend.h | 8 +++---- libraries/gpu/src/gpu/GLBackendShader.cpp | 22 +++++++++++++++++-- .../src/DeferredLightingEffect.cpp | 5 ----- libraries/render-utils/src/Model.cpp | 6 +---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index e86424ceb7..c0bae76d78 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -27,7 +27,7 @@ class GLBackend : public Backend { friend class Context; static void init(); static Backend* createBackend(); - static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings); explicit GLBackend(bool syncCache); GLBackend(); @@ -95,9 +95,9 @@ public: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else - GLuint _transformObject_model = -1; - GLuint _transformCamera_viewInverse = -1; - GLuint _transformCamera_viewport = -1; + GLint _transformObject_model = -1; + GLint _transformCamera_viewInverse = -1; + GLint _transformCamera_viewport = -1; #endif GLShader(); diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 5fa0e277e5..9c0cf7628d 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -541,7 +541,12 @@ ElementResource getFormatFromGLUniform(GLenum gltype) { }; -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + , Shader::SlotSet& fakeBuffers +#endif +) { GLint uniformsCount = 0; #if (GPU_FEATURE_PROFILE == GPU_LEGACY) @@ -582,6 +587,15 @@ int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, S } if (elementResource._resource == Resource::BUFFER) { +#if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // if in legacy profile, we fake the uniform buffer with an array + // this is where we detect it assuming it's explicitely assinged a binding + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + // found one buffer! + fakeBuffers.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } +#endif uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); } else { // For texture/Sampler, the location is the actual binding value @@ -736,8 +750,12 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; +#if (GPU_FEATURE_PROFILE == GPU_CORE) makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers); - +#else + makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers, buffers); +#endif + Shader::SlotSet inputs; makeInputSlots(object->_program, slotBindings, inputs); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 7563609026..3fc0a4c46a 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -614,13 +614,8 @@ void DeferredLightingEffect::loadLightProgram(const char* vertSource, const char locations.texcoordMat = program->getUniforms().findLocation("texcoordMat"); locations.coneParam = program->getUniforms().findLocation("coneParam"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); locations.atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit"); -#else - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); - locations.atmosphereBufferUnit = program->getUniforms().findLocation("atmosphereBufferUnit"); -#endif auto state = std::make_shared(); if (lightVolume) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1e0e04c776..1e4f3f7190 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -187,13 +187,9 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); -#if (GPU_FEATURE_PROFILE == GPU_CORE) locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); -#else - locations.materialBufferUnit = program->getUniforms().findLocation("materialBuffer"); - locations.lightBufferUnit = program->getUniforms().findLocation("lightBuffer"); -#endif + locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); locations.clusterIndices = program->getInputs().findLocation("clusterIndices");; From 0bc94158a8031fa022ae687ea4ea1a8258a82fd7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Jul 2015 21:09:14 -0700 Subject: [PATCH 071/109] make the height of the tetrahedrons in 'fatten' mode less tall. --- tools/vhacd-util/src/VHACDUtil.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 2f8175cbb6..02b8e212be 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -125,12 +125,14 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, continue; } + // from the middle of the triangle, pull a point down to form a tetrahedron. float dropAmount = 0; dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); + dropAmount *= 0.25f; - glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); // a point 1 meter below the average of this triangle's points + glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh From c8f398024eb21f49959ab2fc92d2e3a4f95a1fbb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 30 Jul 2015 21:16:07 -0700 Subject: [PATCH 072/109] replace a magic number with a constant --- tools/vhacd-util/src/VHACDUtil.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 02b8e212be..6743ed6a9f 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -12,6 +12,8 @@ #include #include "VHACDUtil.h" +const float collisionTetrahedronScale = 0.25f; + // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. @@ -130,7 +132,7 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); - dropAmount *= 0.25f; + dropAmount *= collisionTetrahedronScale; glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); int index3 = result.vertices.size(); From 5bae9843f5ee7fe3220d54bbf343490fa4c6ef5c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 31 Jul 2015 09:34:50 -0700 Subject: [PATCH 073/109] code review --- tools/vhacd-util/src/VHACDUtil.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 6743ed6a9f..1e7f9df7c1 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -12,7 +12,7 @@ #include #include "VHACDUtil.h" -const float collisionTetrahedronScale = 0.25f; +const float COLLISION_TETRAHEDRON_SCALE = 0.25f; // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put @@ -132,9 +132,9 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, dropAmount = glm::max(glm::length(p1 - p0), dropAmount); dropAmount = glm::max(glm::length(p2 - p1), dropAmount); dropAmount = glm::max(glm::length(p0 - p2), dropAmount); - dropAmount *= collisionTetrahedronScale; + dropAmount *= COLLISION_TETRAHEDRON_SCALE; - glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); + glm::vec3 p3 = av - glm::vec3(0.0f, dropAmount, 0.0f); int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh From 67760fad79897fa3151bd3b265a484f9c8b121d8 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 31 Jul 2015 09:27:25 -0700 Subject: [PATCH 074/109] Add gettable naturalPosition property for model entities --- libraries/entities/src/EntityItemProperties.cpp | 9 ++++++++- libraries/entities/src/EntityItemProperties.h | 6 +++++- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 61253ba6ba..caae0203ae 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -114,7 +114,8 @@ _glowLevelChanged(false), _localRenderAlphaChanged(false), _defaultSettings(true), -_naturalDimensions(1.0f, 1.0f, 1.0f) +_naturalDimensions(1.0f, 1.0f, 1.0f), +_naturalPosition(0.0f, 0.0f, 0.0f) { } @@ -128,6 +129,11 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting } } +void EntityItemProperties::setNaturalPosition(const glm::vec3& min, const glm::vec3& max) { + glm::vec3 radius = (max - min) / 2.0f; + _naturalPosition = max - radius; +} + bool EntityItemProperties::animationSettingsChanged() const { return _animationSettingsChanged; } @@ -378,6 +384,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions); if (!skipDefaults) { COPY_PROPERTY_TO_QSCRIPTVALUE(naturalDimensions); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(naturalPosition); } COPY_PROPERTY_TO_QSCRIPTVALUE(rotation); COPY_PROPERTY_TO_QSCRIPTVALUE(velocity); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4532ffd67b..94e0f0fac8 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -192,7 +192,10 @@ public: const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } - + + const glm::vec3& getNaturalPosition() const { return _naturalPosition; } + void setNaturalPosition(const glm::vec3& min, const glm::vec3& max); + const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } @@ -232,6 +235,7 @@ private: QVector _sittingPoints; QStringList _textureNames; glm::vec3 _naturalDimensions; + glm::vec3 _naturalPosition; }; Q_DECLARE_METATYPE(EntityItemProperties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 36fcc17a71..c40f7edd47 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -118,6 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); + results.setNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } From aab1f708002c5e15d19e7b0371fa05a2faccd6e7 Mon Sep 17 00:00:00 2001 From: bwent Date: Fri, 31 Jul 2015 10:47:05 -0700 Subject: [PATCH 075/109] Renaming vars --- libraries/entities/src/EntityItemProperties.cpp | 6 +++--- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index caae0203ae..ebe4ac6014 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -129,9 +129,9 @@ void EntityItemProperties::setSittingPoints(const QVector& sitting } } -void EntityItemProperties::setNaturalPosition(const glm::vec3& min, const glm::vec3& max) { - glm::vec3 radius = (max - min) / 2.0f; - _naturalPosition = max - radius; +void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { + glm::vec3 halfDimension = (max - min) / 2.0f; + _naturalPosition = max - halfDimension; } bool EntityItemProperties::animationSettingsChanged() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 94e0f0fac8..3ce6040d19 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -194,7 +194,7 @@ public: void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } const glm::vec3& getNaturalPosition() const { return _naturalPosition; } - void setNaturalPosition(const glm::vec3& min, const glm::vec3& max); + void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); const QStringList& getTextureNames() const { return _textureNames; } void setTextureNames(const QStringList& value) { _textureNames = value; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c40f7edd47..12f5ffe190 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -118,7 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); - results.setNaturalPosition(meshExtents.minimum, meshExtents.maximum); + results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } From dff6b0a456a96cd7eb37b9069d811ea133c3f0df Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Tue, 30 Jun 2015 14:32:37 -0700 Subject: [PATCH 076/109] Fix isFacingAvatar property on BillboardOverlay. --- .../src/ui/overlays/BillboardOverlay.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index d26ecc5c67..37e197130b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -20,6 +20,9 @@ #include "Application.h" #include "GeometryUtil.h" +#include + + BillboardOverlay::BillboardOverlay() { _isLoaded = false; } @@ -45,9 +48,28 @@ void BillboardOverlay::render(RenderArgs* args) { glm::quat rotation; if (_isFacingAvatar) { + // LOL, quaternions are hard. // rotate about vertical to face the camera +// glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); +// dPos = glm::normalize(dPos); +// rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); rotation = args->_viewFrustum->getOrientation(); rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); +// float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); +// glm::vec3 zAxis = glm::vec3(0, 0, 1); +// rotation = rotationBetween(zAxis, dPos); +// glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); +// rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); +// float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); +// glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); +// float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); +// glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); +// glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), +// glm::vec3(0, 1, 0)); +// rotation = glm::quat_cast(matrix); +// rotation = yawQuat * pitchQuat; +// glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); +// rotation = glm::quat(glm::vec3(pitch, yaw, 0)); rotation *= getRotation(); } else { rotation = getRotation(); From 173a79867ce16f78dfe28b8d8affb06b9cde5909 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 13 Jul 2015 16:38:16 -0700 Subject: [PATCH 077/109] Extend Overlays API to allow for 3D UI panels. Currently, only BillboardOverlays can be added to a panel, but more types of overlays will be supported in the future. --- examples/example/ui/floatingUIExample.js | 173 ++++++++++++++++++ .../src/ui/overlays/BillboardOverlay.cpp | 50 ++++- interface/src/ui/overlays/BillboardOverlay.h | 5 +- interface/src/ui/overlays/FloatingUIPanel.cpp | 89 +++++++++ interface/src/ui/overlays/FloatingUIPanel.h | 47 +++++ interface/src/ui/overlays/Overlays.cpp | 139 ++++++++++---- interface/src/ui/overlays/Overlays.h | 24 +++ interface/src/ui/overlays/PanelAttachable.h | 44 +++++ 8 files changed, 530 insertions(+), 41 deletions(-) create mode 100644 examples/example/ui/floatingUIExample.js create mode 100644 interface/src/ui/overlays/FloatingUIPanel.cpp create mode 100644 interface/src/ui/overlays/FloatingUIPanel.h create mode 100644 interface/src/ui/overlays/PanelAttachable.h diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js new file mode 100644 index 0000000000..09deca4ec7 --- /dev/null +++ b/examples/example/ui/floatingUIExample.js @@ -0,0 +1,173 @@ +// +// floatingUI.js +// examples/example/ui +// +// Created by Alexander Otavka +// 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 +// + +Script.include(["../../libraries/globals.js"]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; +var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; + +var BLANK_ROTATION = { x: 0, y: 0, z: 0, w: 0 }; + +function isBlank(rotation) { + return rotation.x == BLANK_ROTATION.x && + rotation.y == BLANK_ROTATION.y && + rotation.z == BLANK_ROTATION.z && + rotation.w == BLANK_ROTATION.w; +} + +var panel = Overlays.addPanel({ + offsetPosition: { x: 0, y: 0, z: 1 }, +}); + +var panelChildren = []; + +var bg = Overlays.addOverlay("billboard", { + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, +}); +panelChildren.push(bg); + +var redDot = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: -0.15, + z: -0.001 + } +}); +panelChildren.push(redDot); + +var redDot2 = Overlays.addOverlay("billboard", { + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.15, + y: 0, + z: -0.001 + } +}); +panelChildren.push(redDot2); + +var blueSquare = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0, + z: -0.001 + } +}); +panelChildren.push(blueSquare); + +var blueSquare2 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: 0.1, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare2); + +var blueSquare3 = Overlays.addOverlay("billboard", { + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + attachedPanel: panel, + offsetPosition: { + x: -0.01, + y: 0.11, + z: -0.001 + } +}); +panelChildren.push(blueSquare3); + +Controller.mousePressEvent.connect(function(event) { + if (event.isRightButton) { + var newOffsetRotation = BLANK_ROTATION; + if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } + Overlays.editPanel(panel, { + offsetRotation: newOffsetRotation + }); + } else if (event.isLeftButton) { + var pickRay = Camera.computePickRay(event.x, event.y) + var rayPickResult = Overlays.findRayIntersection(pickRay); + print(String(rayPickResult.overlayID)); + if (rayPickResult.intersects) { + for (var i in panelChildren) { + if (panelChildren[i] == rayPickResult.overlayID) { + var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + } + Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); + } + } + } + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deletePanel(panel); +}); \ No newline at end of file diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 37e197130b..9e1a9a44de 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -36,6 +36,13 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } +void BillboardOverlay::update(float deltatime) { + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } +} + void BillboardOverlay::render(RenderArgs* args) { if (!_texture) { _isLoaded = true; @@ -46,15 +53,17 @@ void BillboardOverlay::render(RenderArgs* args) { return; } + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } + glm::quat rotation; if (_isFacingAvatar) { // LOL, quaternions are hard. - // rotate about vertical to face the camera // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); // dPos = glm::normalize(dPos); // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); // glm::vec3 zAxis = glm::vec3(0, 0, 1); // rotation = rotationBetween(zAxis, dPos); @@ -70,9 +79,24 @@ void BillboardOverlay::render(RenderArgs* args) { // rotation = yawQuat * pitchQuat; // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); + // rotate about vertical to be perpendicular to the camera + rotation = args->_viewFrustum->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); rotation *= getRotation(); } else { rotation = getRotation(); + if (getAttachedPanel()) { + rotation *= getAttachedPanel()->getOffsetRotation() * + getAttachedPanel()->getFacingRotation(); +// if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getFacingRotation(); +// } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { +// rotation *= getAttachedPanel()->getOffsetRotation(); +// } else { +// rotation *= Application::getInstance()->getCamera()->getOrientation() * +// glm::quat(0, 0, 1, 0); +// } + } } float imageWidth = _texture->getWidth(); @@ -114,7 +138,7 @@ void BillboardOverlay::render(RenderArgs* args) { Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.setRotation(rotation); - + batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); @@ -171,6 +195,21 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); } + + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } } QScriptValue BillboardOverlay::getProperty(const QString& property) { @@ -183,6 +222,9 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { if (property == "isFacingAvatar") { return _isFacingAvatar; } + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } return Planar3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 15be0419a9..f7bbfd1817 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -15,8 +15,9 @@ #include #include "Planar3DOverlay.h" +#include "PanelAttachable.h" -class BillboardOverlay : public Planar3DOverlay { +class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: BillboardOverlay(); @@ -24,6 +25,8 @@ public: virtual void render(RenderArgs* args); + virtual void update(float deltatime); + // setters void setURL(const QString& url); void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp new file mode 100644 index 0000000000..e655a75f07 --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -0,0 +1,89 @@ +// +// FloatingUIPanel.cpp +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 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 "FloatingUIPanel.h" + +#include +#include + +#include "Application.h" + + +glm::quat FloatingUIPanel::getOffsetRotation() const { + if (getActualOffsetRotation() == glm::quat(0, 0, 0, 0)) { + return Application::getInstance()->getCamera()->getOrientation() * glm::quat(0, 0, 1, 0); + } + return getActualOffsetRotation(); +} + +QScriptValue FloatingUIPanel::getProperty(const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + } + if (property == "offsetRotation") { + return quatToScriptValue(_scriptEngine, getActualOffsetRotation()); + } + if (property == "facingRotation") { + return quatToScriptValue(_scriptEngine, getFacingRotation()); + } + + return QScriptValue(); +} + +void FloatingUIPanel::setProperties(const QScriptValue &properties) { + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } + + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = offsetRotation.property("x"); + QScriptValue y = offsetRotation.property("y"); + QScriptValue z = offsetRotation.property("z"); + QScriptValue w = offsetRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setOffsetRotation(newRotation); + } + } + + QScriptValue facingRotation = properties.property("facingRotation"); + if (offsetRotation.isValid()) { + QScriptValue x = facingRotation.property("x"); + QScriptValue y = facingRotation.property("y"); + QScriptValue z = facingRotation.property("z"); + QScriptValue w = facingRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setFacingRotation(newRotation); + } + } +} diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h new file mode 100644 index 0000000000..7f8d42eb5b --- /dev/null +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -0,0 +1,47 @@ +// +// FloatingUIPanel.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/2/15. +// Copyright 2014 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_FloatingUIPanel_h +#define hifi_FloatingUIPanel_h + +#include +#include +#include + +class FloatingUIPanel : public QObject { + Q_OBJECT +public: + typedef std::shared_ptr Pointer; + + QList children; + + void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + glm::quat getOffsetRotation() const; + glm::quat getActualOffsetRotation() const { return _offsetRotation; } + glm::quat getFacingRotation() const { return _facingRotation; } + + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; }; + void setOffsetRotation(glm::quat rotation) { _offsetRotation = rotation; }; + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; }; + + QScriptValue getProperty(const QString& property); + void setProperties(const QScriptValue& properties); + +private: + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); + glm::quat _offsetRotation = glm::quat(0, 0, 0, 0); + glm::quat _facingRotation = glm::quat(1, 0, 0, 0); + QScriptEngine* _scriptEngine; +}; + +#endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index ff218db844..fe489847ee 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -48,6 +48,7 @@ Overlays::~Overlays() { } _overlaysHUD.clear(); _overlaysWorld.clear(); + _panels.clear(); } cleanupOverlaysToDelete(); @@ -124,11 +125,36 @@ void Overlays::renderHUD(RenderArgs* renderArgs) { } } +Overlay::Pointer Overlays::getOverlay(unsigned int id) const { + if (_overlaysHUD.contains(id)) { + return _overlaysHUD[id]; + } + if (_overlaysWorld.contains(id)) { + return _overlaysWorld[id]; + } + return nullptr; +} + +void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property) { + if (PanelAttachable* attachable = dynamic_cast(overlay)) { + if (property.isValid()) { + unsigned int attachedPanelId = property.toVariant().toUInt(); + FloatingUIPanel* panel = nullptr; + if (_panels.contains(attachedPanelId)) { + panel = _panels[attachedPanelId].get(); + panel->children.append(overlayId); + attachable->setAttachedPanel(panel); + } else { + attachable->getAttachedPanel()->children.removeAll(overlayId); + attachable->setAttachedPanel(nullptr); + } + } + } +} + unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - unsigned int thisID = 0; Overlay* thisOverlay = NULL; - - bool created = true; + if (type == "image") { thisOverlay = new ImageOverlay(); } else if (type == "text") { @@ -153,16 +179,15 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new ModelOverlay(); } else if (type == "billboard") { thisOverlay = new BillboardOverlay(); - } else { - created = false; } - if (created) { + if (thisOverlay) { thisOverlay->setProperties(properties); - thisID = addOverlay(thisOverlay); + unsigned int overlayId = addOverlay(thisOverlay); + setAttachedPanel(thisOverlay, overlayId, properties.property("attachedPanel")); + return overlayId; } - - return thisID; + return 0; } unsigned int Overlays::addOverlay(Overlay* overlay) { @@ -189,17 +214,12 @@ unsigned int Overlays::addOverlay(Overlay* overlay) { } else { _overlaysHUD[thisID] = overlayPointer; } - + return thisID; } unsigned int Overlays::cloneOverlay(unsigned int id) { - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { return addOverlay(thisOverlay->createClone()); @@ -210,14 +230,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { QWriteLocker lock(&_lock); - Overlay::Pointer thisOverlay; - - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } + Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { if (thisOverlay->is3D()) { auto overlay3D = std::static_pointer_cast(thisOverlay); @@ -239,6 +253,8 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } + setAttachedPanel(thisOverlay.get(), id, properties.property("attachedPanel")); + return true; } return false; @@ -302,15 +318,18 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) { OverlayPropertyResult result; - Overlay::Pointer thisOverlay; + Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } if (thisOverlay) { - result.value = thisOverlay->getProperty(property); + if (property == "attachedPanel") { + if (FloatingUIPanel* panel = dynamic_cast(thisOverlay.get())) { + result.value = _panels.key(FloatingUIPanel::Pointer(panel)); + } else { + result.value = 0; + } + } else { + result.value = thisOverlay->getProperty(property); + } } return result; } @@ -456,12 +475,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R bool Overlays::isLoaded(unsigned int id) { QReadLocker lock(&_lock); - Overlay::Pointer thisOverlay = NULL; - if (_overlaysHUD.contains(id)) { - thisOverlay = _overlaysHUD[id]; - } else if (_overlaysWorld.contains(id)) { - thisOverlay = _overlaysWorld[id]; - } else { + Overlay::Pointer thisOverlay = getOverlay(id); + if (!thisOverlay) { return false; // not found } return thisOverlay->isLoaded(); @@ -483,3 +498,55 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { } return QSizeF(0.0f, 0.0f); } + +unsigned int Overlays::addPanel(FloatingUIPanel* panel) { + QWriteLocker lock(&_lock); + + FloatingUIPanel::Pointer panelPointer(panel); + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + _panels[thisID] = panelPointer; + + return thisID; +} + +unsigned int Overlays::addPanel(const QScriptValue& properties) { + FloatingUIPanel* panel = new FloatingUIPanel(); + panel->init(_scriptEngine); + panel->setProperties(properties); + return addPanel(panel); +} + +void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) { + if (_panels.contains(panelId)) { + _panels[panelId]->setProperties(properties); + } +} + +OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) { + OverlayPropertyResult result; + if (_panels.contains(panelId)) { + FloatingUIPanel::Pointer thisPanel = _panels[panelId]; + QReadLocker lock(&_lock); + result.value = thisPanel->getProperty(property); + } + return result; +} + + +void Overlays::deletePanel(unsigned int panelId) { + FloatingUIPanel::Pointer panelToDelete; + + { + QWriteLocker lock(&_lock); + if (_panels.contains(panelId)) { + panelToDelete = _panels.take(panelId); + } else { + return; + } + } + + while (!panelToDelete->children.isEmpty()) { + deleteOverlay(panelToDelete->children.takeLast()); + } +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index cd5b0f1d10..2ba2ec8f45 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -16,6 +16,9 @@ #include "Overlay.h" +#include "FloatingUIPanel.h" +#include "PanelAttachable.h" + class PickRay; class OverlayPropertyResult { @@ -90,12 +93,33 @@ public slots: /// overlay; in meters if it is a 3D text overlay QSizeF textSize(unsigned int id, const QString& text) const; + + /// adds a panel that has already been created + unsigned int addPanel(FloatingUIPanel* panel); + + /// creates and adds a panel based on a set of properties + unsigned int addPanel(const QScriptValue& properties); + + /// edit the properties of a panel + void editPanel(unsigned int panelId, const QScriptValue& properties); + + /// get a property of a panel + OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property); + + /// deletes a panel and all child overlays + void deletePanel(unsigned int panelId); + private: void cleanupOverlaysToDelete(); + Overlay::Pointer getOverlay(unsigned int id) const; + void setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property); + QMap _overlaysHUD; QMap _overlaysWorld; + QMap _panels; QList _overlaysToDelete; unsigned int _nextOverlayID; + QReadWriteLock _lock; QReadWriteLock _deleteLock; QScriptEngine* _scriptEngine; diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h new file mode 100644 index 0000000000..c364d62fa7 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -0,0 +1,44 @@ +// +// PanelAttachable.h +// interface/src/ui/overlays +// +// Created by Zander Otavka on 7/1/15. +// Copyright 2014 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_PanelAttachable_h +#define hifi_PanelAttachable_h + +#include "FloatingUIPanel.h" + +#include + +class PanelAttachable { +public: + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + + FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } + void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + + glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) { + if (getAttachedPanel()) { + glm::vec3 totalOffsetPosition = + getAttachedPanel()->getFacingRotation() * getOffsetPosition() + + getAttachedPanel()->getOffsetPosition(); + + return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + + avatarPosition; + } + return glm::vec3(); + } + +private: + FloatingUIPanel* _attachedPanel = nullptr; + glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); +}; + +#endif // hifi_PanelAttachable_h From ed7fc07ab1140c2db7f7fdd22e060e4db27cb595 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 15 Jul 2015 14:08:09 -0700 Subject: [PATCH 078/109] Fix cloning bug for BillboardOverlay. --- .../src/ui/overlays/BillboardOverlay.cpp | 1 + interface/src/ui/overlays/PanelAttachable.cpp | 33 +++++++++++++++++++ interface/src/ui/overlays/PanelAttachable.h | 17 +++------- interface/src/ui/overlays/Planar3DOverlay.cpp | 9 ++++- interface/src/ui/overlays/Planar3DOverlay.h | 4 +-- 5 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 interface/src/ui/overlays/PanelAttachable.cpp diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 9e1a9a44de..18d0307743 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -29,6 +29,7 @@ BillboardOverlay::BillboardOverlay() { BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : Planar3DOverlay(billboardOverlay), + PanelAttachable(billboardOverlay), _url(billboardOverlay->_url), _texture(billboardOverlay->_texture), _fromImage(billboardOverlay->_fromImage), diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp new file mode 100644 index 0000000000..172880db97 --- /dev/null +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -0,0 +1,33 @@ +// +// PanelAttachable.cpp +// hifi +// +// Created by Zander Otavka on 7/15/15. +// +// + +#include "PanelAttachable.h" + +PanelAttachable::PanelAttachable() : + _attachedPanel(nullptr), + _offsetPosition(glm::vec3()) +{ +} + +PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : + _attachedPanel(panelAttachable->_attachedPanel), + _offsetPosition(panelAttachable->_offsetPosition) +{ +} + +glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const { + if (getAttachedPanel()) { + glm::vec3 totalOffsetPosition = + getAttachedPanel()->getFacingRotation() * getOffsetPosition() + + getAttachedPanel()->getOffsetPosition(); + + return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + + avatarPosition; + } + return glm::vec3(); +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index c364d62fa7..29b0673157 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -18,26 +18,19 @@ class PanelAttachable { public: + PanelAttachable(); + PanelAttachable(const PanelAttachable* panelAttachable); + glm::vec3 getOffsetPosition() const { return _offsetPosition; } void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } - glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) { - if (getAttachedPanel()) { - glm::vec3 totalOffsetPosition = - getAttachedPanel()->getFacingRotation() * getOffsetPosition() + - getAttachedPanel()->getOffsetPosition(); - - return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + - avatarPosition; - } - return glm::vec3(); - } + glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; private: - FloatingUIPanel* _attachedPanel = nullptr; + FloatingUIPanel* _attachedPanel; glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 0ca092ba23..354991b596 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -14,8 +14,15 @@ #include #include +Planar3DOverlay::Planar3DOverlay() : + Base3DOverlay(), + _dimensions{1.0f, 1.0f} +{ +} + Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : - Base3DOverlay(planar3DOverlay) + Base3DOverlay(planar3DOverlay), + _dimensions(planar3DOverlay->_dimensions) { } diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index fe8c513efd..08a7121e91 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -17,7 +17,7 @@ class Planar3DOverlay : public Base3DOverlay { Q_OBJECT public: - Planar3DOverlay() {} + Planar3DOverlay(); Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); AABox getBounds() const; @@ -32,7 +32,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face); protected: - glm::vec2 _dimensions{1.0f, 1.0f}; + glm::vec2 _dimensions; }; From 377a1a54aeaa8e9343325b7b589b056d6a8d242d Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Wed, 15 Jul 2015 15:32:14 -0700 Subject: [PATCH 079/109] Add abstraction layer for Overlays.h. `examples/libraries/overlayUtils.js` allows you to manage overlays in an object oriented manner. Instead of: var billboard = Overlays.addOverlay("billboard", { visible: false }); ... Overlays.editOverlay(billboard, { visible: true }); ... Overlays.deleteOverlay(billboard); You can now do: var billboard = new BillboardOverlay({ visible: false }); ... billboard.visible = true; ... billboard.destroy(); --- examples/example/ui/floatingUIExample.js | 98 ++++----- examples/libraries/overlayUtils.js | 257 ++++++++++++++++++++++- interface/src/ui/overlays/Overlays.h | 6 + 3 files changed, 298 insertions(+), 63 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 09deca4ec7..3b555efce3 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -9,7 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include(["../../libraries/globals.js"]); +Script.include([ + "../../libraries/globals.js", + "../../libraries/overlayUtils.js", +]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; @@ -24,13 +27,11 @@ function isBlank(rotation) { rotation.w == BLANK_ROTATION.w; } -var panel = Overlays.addPanel({ +var panel = new FloatingUIPanel({ offsetPosition: { x: 0, y: 0, z: 1 }, }); -var panelChildren = []; - -var bg = Overlays.addOverlay("billboard", { +var bg = panel.addChild(new BillboardOverlay({ url: BG_IMAGE_URL, dimensions: { x: 0.5, @@ -40,11 +41,9 @@ var bg = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, -}); -panelChildren.push(bg); +})); -var redDot = Overlays.addOverlay("billboard", { +var redDot = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -54,16 +53,14 @@ var redDot = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: -0.15, z: -0.001 } -}); -panelChildren.push(redDot); +})); -var redDot2 = Overlays.addOverlay("billboard", { +var redDot2 = panel.addChild(new BillboardOverlay({ url: RED_DOT_IMAGE_URL, dimensions: { x: 0.1, @@ -73,16 +70,14 @@ var redDot2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: -0.15, y: 0, z: -0.001 } -}); -panelChildren.push(redDot2); +})); -var blueSquare = Overlays.addOverlay("billboard", { +var blueSquare = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -92,16 +87,14 @@ var blueSquare = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0, z: -0.001 } -}); -panelChildren.push(blueSquare); +})); -var blueSquare2 = Overlays.addOverlay("billboard", { +var blueSquare2 = panel.addChild(new BillboardOverlay({ url: BLUE_SQUARE_IMAGE_URL, dimensions: { x: 0.1, @@ -111,63 +104,46 @@ var blueSquare2 = Overlays.addOverlay("billboard", { visible: true, alpha: 1.0, ignoreRayIntersection: false, - attachedPanel: panel, offsetPosition: { x: 0.1, y: 0.11, z: -0.001 } -}); -panelChildren.push(blueSquare2); +})); -var blueSquare3 = Overlays.addOverlay("billboard", { - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - attachedPanel: panel, - offsetPosition: { - x: -0.01, - y: 0.11, - z: -0.001 - } -}); -panelChildren.push(blueSquare3); +var blueSquare3 = blueSquare2.clone(); +blueSquare3.offsetPosition = { + x: -0.01, + y: 0.11, + z: -0.001 +}; +blueSquare3.ignoreRayIntersection = false; Controller.mousePressEvent.connect(function(event) { if (event.isRightButton) { - var newOffsetRotation = BLANK_ROTATION; - if (isBlank(Overlays.getPanelProperty(panel, "offsetRotation"))) { + var newOffsetRotation; + print(JSON.stringify(panel.offsetRotation)) + if (isBlank(panel.offsetRotation)) { newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); + } else { + newOffsetRotation = BLANK_ROTATION; } - Overlays.editPanel(panel, { - offsetRotation: newOffsetRotation - }); + panel.offsetRotation = newOffsetRotation; } else if (event.isLeftButton) { var pickRay = Camera.computePickRay(event.x, event.y) - var rayPickResult = Overlays.findRayIntersection(pickRay); - print(String(rayPickResult.overlayID)); - if (rayPickResult.intersects) { - for (var i in panelChildren) { - if (panelChildren[i] == rayPickResult.overlayID) { - var oldPos = Overlays.getProperty(rayPickResult.overlayID, "offsetPosition"); - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - } - Overlays.editOverlay(rayPickResult.overlayID, { offsetPosition: newPos }); - } + var overlay = panel.findRayIntersection(pickRay); + if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 } + overlay.offsetPosition = newPos; } } }); Script.scriptEnding.connect(function() { - Overlays.deletePanel(panel); + panel.destroy(); }); \ No newline at end of file diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 636ea40825..3acab0103b 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -1,6 +1,39 @@ +// +// overlayUtils.js +// examples/libraries +// +// Modified by Zander Otavka on 7/15/15 +// Copyright 2014 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + /** - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. + * DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. + * + * OverlayGroup provides a way to create composite overlays and control their + * position relative to a settable rootPosition and rootRotation. */ OverlayGroup = function(opts) { var that = {}; @@ -62,3 +95,223 @@ OverlayGroup = function(opts) { return that; }; + + +/** + * Object oriented abstraction layer for overlays. + * + * Usage: + * // Create an overlay + * var billboard = new BillboardOverlay({ + * visible: true, + * isFacingAvatar: true, + * ignoreRayIntersections: false + * }); + * + * // Get a property + * var isVisible = billboard.visible; + * + * // Set a single property + * billboard.position = { x: 1, y: 3, z: 2 }; + * + * // Set multiple properties at the same time + * billboard.setProperties({ + * url: "http://images.com/overlayImage.jpg", + * dimensions: { x: 2, y: 2 } + * }); + * + * // Clone an overlay + * var clonedBillboard = billboard.clone(); + * + * // Remove an overlay from the world + * billboard.destroy(); + * + * // Remember, there is a poor orphaned JavaScript object left behind. You should remove any + * // references to it so you don't accidentally try to modify an overlay that isn't there. + * billboard = undefined; + */ +(function() { + var ABSTRACT = null; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.apply(this, [type, params]); + }; + } else { + that = function(params) { + superclass.apply(this, [type, params]); + }; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: true + }); + }); + + return that; + } + + + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PANEL_ATTACHABLE_FIELDS = ["attachedPanel"]; + + // TODO: finish exposing all overlay classes. + + var Overlay = (function() { + var BaseOverlay = (function() { + var that = function(type, params) { + Object.apply(this, []); + if (type && params) { + this._type = type; + this._id = Overlays.addOverlay(type, params); + } else { + this._type = ""; + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype = new Object(); + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "overlayType", { + get: function() { + return this._type; + } + }); + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + var clone = new this.constructor(); + clone._type = this._type; + clone._id = Overlays.cloneOverlay(this._id); + if (this._attachedPanelPointer) { + this._attachedPanelPointer.addChild(clone); + } + return clone; + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return that; + }()); + + return generateOverlayClass(BaseOverlay, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + }()); + + var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + BillboardOverlay = generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar", "offsetPosition" + ].concat(PANEL_ATTACHABLE_FIELDS)); +}()); + + +/** + * Object oriented abstraction layer for panels. + */ +FloatingUIPanel = (function() { + var that = function(params) { + this._id = Overlays.addPanel(params); + this._children = []; + }; + + var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; + FIELDS.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getPanelProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + Object.defineProperty(that.prototype, "children", { + get: function() { + return this._children.slice(); + } + }) + + that.prototype.addChild = function(overlay) { + overlay.attachedPanel = this._id; + overlay._attachedPanelPointer = this; + this._children.push(overlay); + return overlay; + }; + + that.prototype.removeChild = function(overlay) { + var i = this._children.indexOf(overlay); + if (i >= 0) { + overlay.attachedPanel = 0; + overlay._attachedPanelPointer = null; + this._children.splice(i, 1); + } + }; + + that.prototype.setVisible = function(visible) { + for (var i in this._children) { + this._children[i].visible = visible; + } + }; + + that.prototype.setProperties = function(properties) { + Overlays.editPanel(this._id, properties); + }; + + that.prototype.destroy = function() { + Overlays.deletePanel(this._id); + var i = _panels.indexOf(this); + if (i >= 0) { + _panels.splice(i, 1); + } + }; + + that.prototype.findRayIntersection = function(pickRay) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + if (rayPickResult.intersects) { + for (var i in this._children) { + if (this._children[i]._id == rayPickResult.overlayID) { + return this._children[i]; + } + } + } + return null; + }; + + return that; +}()); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 2ba2ec8f45..80da2e6db1 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -2,8 +2,14 @@ // Overlays.h // interface/src/ui/overlays // +// Modified by Zander Otavka on 7/15/15 // Copyright 2014 High Fidelity, Inc. // +// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts. +// +// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the +// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // From c4cb6fba744b2eea540918fb755f91ddc0c162bf Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 14:29:53 -0700 Subject: [PATCH 080/109] Improve BillboardOverlay::findRayIntersection. --- .../src/ui/overlays/BillboardOverlay.cpp | 94 ++++++++----------- interface/src/ui/overlays/BillboardOverlay.h | 1 + interface/src/ui/overlays/PanelAttachable.cpp | 17 ++++ interface/src/ui/overlays/PanelAttachable.h | 1 + 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 18d0307743..be8fab6362 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -38,9 +38,11 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : } void BillboardOverlay::update(float deltatime) { - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); + if (getVisible()) { + glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); + if (newPos != glm::vec3()) { + setPosition(newPos); + } } } @@ -59,46 +61,7 @@ void BillboardOverlay::render(RenderArgs* args) { setPosition(newPos); } - glm::quat rotation; - if (_isFacingAvatar) { - // LOL, quaternions are hard. -// glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); -// dPos = glm::normalize(dPos); -// rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); -// float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); -// glm::vec3 zAxis = glm::vec3(0, 0, 1); -// rotation = rotationBetween(zAxis, dPos); -// glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); -// rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); -// float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); -// glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); -// float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); -// glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); -// glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), -// glm::vec3(0, 1, 0)); -// rotation = glm::quat_cast(matrix); -// rotation = yawQuat * pitchQuat; -// glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); -// rotation = glm::quat(glm::vec3(pitch, yaw, 0)); - // rotate about vertical to be perpendicular to the camera - rotation = args->_viewFrustum->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - rotation *= getRotation(); - } else { - rotation = getRotation(); - if (getAttachedPanel()) { - rotation *= getAttachedPanel()->getOffsetRotation() * - getAttachedPanel()->getFacingRotation(); -// if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { -// rotation *= getAttachedPanel()->getFacingRotation(); -// } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { -// rotation *= getAttachedPanel()->getOffsetRotation(); -// } else { -// rotation *= Application::getInstance()->getCamera()->getOrientation() * -// glm::quat(0, 0, 1, 0); -// } - } - } + glm::quat rotation = calculateRotation(args->_viewFrustum->getOrientation()); float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -143,8 +106,10 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) + ); batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } @@ -239,17 +204,40 @@ void BillboardOverlay::setBillboardURL(const QString& url) { _isLoaded = false; } +glm::quat BillboardOverlay::calculateRotation(glm::quat cameraOrientation) const { + if (_isFacingAvatar) { + // LOL, quaternions are hard. + // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); + // dPos = glm::normalize(dPos); + // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); + // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); + // glm::vec3 zAxis = glm::vec3(0, 0, 1); + // rotation = rotationBetween(zAxis, dPos); + // glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); + // rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); + // float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); + // glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); + // float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); + // glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); + // glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), + // glm::vec3(0, 1, 0)); + // rotation = glm::quat_cast(matrix); + // rotation = yawQuat * pitchQuat; + // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); + // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); + // rotate about vertical to be perpendicular to the camera + glm::quat rotation = cameraOrientation; + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + return rotation * getRotation(); + } + return getTranslatedRotation(getRotation()); +} + bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) { - if (_texture && _texture->isLoaded()) { - glm::quat rotation = getRotation(); - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - } - + glm::quat rotation = + calculateRotation(Application::getInstance()->getCamera()->getRotation()); // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index f7bbfd1817..73e9db4c1b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -41,6 +41,7 @@ public: private: void setBillboardURL(const QString& url); + glm::quat calculateRotation(glm::quat cameraOrientation) const; QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 172880db97..1beea6b74e 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -31,3 +31,20 @@ glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const } return glm::vec3(); } + +glm::quat PanelAttachable::getTranslatedRotation(glm::quat offsetRotation) const { + glm::quat rotation = offsetRotation; + if (getAttachedPanel()) { + rotation *= getAttachedPanel()->getOffsetRotation() * + getAttachedPanel()->getFacingRotation(); + // if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { + // rotation *= getAttachedPanel()->getFacingRotation(); + // } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { + // rotation *= getAttachedPanel()->getOffsetRotation(); + // } else { + // rotation *= Application::getInstance()->getCamera()->getOrientation() * + // glm::quat(0, 0, 1, 0); + // } + } + return rotation; +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 29b0673157..ab8b8a4276 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -28,6 +28,7 @@ public: void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; + glm::quat getTranslatedRotation(glm::quat offsetRotation) const; private: FloatingUIPanel* _attachedPanel; From c77a91eb63f0b5ab7fff08b201b2fb15aac1edf7 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 14:31:38 -0700 Subject: [PATCH 081/109] Expose address bar toggling to scripts. --- interface/src/Application.cpp | 1 + interface/src/Application.h | 4 ++++ .../DialogsManagerScriptingInterface.cpp | 18 +++++++++++++++ .../DialogsManagerScriptingInterface.h | 23 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 interface/src/scripting/DialogsManagerScriptingInterface.cpp create mode 100644 interface/src/scripting/DialogsManagerScriptingInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 047596e40a..995c49b891 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3758,6 +3758,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); diff --git a/interface/src/Application.h b/interface/src/Application.h index d1886862d2..56c126462a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -49,6 +49,7 @@ #include "avatar/MyAvatar.h" #include "devices/SixenseManager.h" #include "scripting/ControllerScriptingInterface.h" +#include "scripting/DialogsManagerScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "ui/AudioStatsDialog.h" #include "ui/BandwidthDialog.h" @@ -69,6 +70,7 @@ #include "UndoStackScriptingInterface.h" #include "gpu/Context.h" + #include "render/Engine.h" class QGLWidget; @@ -643,6 +645,8 @@ private: ApplicationOverlay _applicationOverlay; ApplicationCompositor _compositor; int _numFramesSinceLastResize = 0; + + DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); }; #endif // hifi_Application_h diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.cpp b/interface/src/scripting/DialogsManagerScriptingInterface.cpp new file mode 100644 index 0000000000..abbedb456e --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.cpp @@ -0,0 +1,18 @@ +// +// DialogsManagerScriptingInterface.cpp +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// 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 "DialogsManagerScriptingInterface.h" + +#include "ui/DialogsManager.h" + +void DialogsManagerScriptingInterface::toggleAddressBar() { + QMetaObject::invokeMethod(DependencyManager::get().data(), "toggleAddressBar", Qt::QueuedConnection); +} diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h new file mode 100644 index 0000000000..9a844f79de --- /dev/null +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -0,0 +1,23 @@ +// +// DialogsManagerScriptingInterface.h +// interface/src/scripting +// +// Created by Zander Otavka on 7/17/15. +// 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 +// + +#ifndef hifi_DialogsManagerScriptInterface_h +#define hifi_DialogsManagerScriptInterface_h + +#include + +class DialogsManagerScriptingInterface : public QObject { + Q_OBJECT +public slots: + void toggleAddressBar(); +}; + +#endif From 052415534990607d46aa5ab0af11b9919f8d9d3b Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 17 Jul 2015 15:35:11 -0700 Subject: [PATCH 082/109] Expose face tracking to the script engine. --- interface/src/Application.cpp | 2 ++ interface/src/devices/FaceTracker.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 995c49b891..acdfd8cfc9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3763,6 +3763,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); + scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index 193262d121..7126d19ca8 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -47,7 +47,6 @@ public: bool isMuted() const { return _isMuted; } void setIsMuted(bool isMuted) { _isMuted = isMuted; } - void toggleMute(); static float getEyeDeflection() { return _eyeDeflection.get(); } static void setEyeDeflection(float eyeDeflection); @@ -57,6 +56,8 @@ signals: public slots: virtual void setEnabled(bool enabled) = 0; + void toggleMute(); + bool getMuted() { return _isMuted; } protected: virtual ~FaceTracker() {}; From 1655dea2c3409b2ff4793639de2531213098a3c9 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 11:44:03 -0700 Subject: [PATCH 083/109] Fix BillboardOverlay first person render bug. --- interface/src/ui/overlays/BillboardOverlay.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index be8fab6362..c5e73a73e2 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -20,6 +20,8 @@ #include "Application.h" #include "GeometryUtil.h" +#include "DeferredLightingEffect.h" + #include @@ -100,12 +102,13 @@ void BillboardOverlay::render(RenderArgs* args) { if (batch) { Transform transform = _transform; - transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.setRotation(rotation); + transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); + DependencyManager::get()->bindSimpleProgram(*batch, true, true, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) From e5b870121534d8d6236278ce8abe04abcda15d25 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 11:45:15 -0700 Subject: [PATCH 084/109] Add toggle event for address bar. --- .../scripting/DialogsManagerScriptingInterface.cpp | 12 ++++++++++-- .../src/scripting/DialogsManagerScriptingInterface.h | 6 ++++++ interface/src/ui/DialogsManager.cpp | 1 + interface/src/ui/DialogsManager.h | 3 +++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.cpp b/interface/src/scripting/DialogsManagerScriptingInterface.cpp index abbedb456e..80a8b4ac7c 100644 --- a/interface/src/scripting/DialogsManagerScriptingInterface.cpp +++ b/interface/src/scripting/DialogsManagerScriptingInterface.cpp @@ -11,8 +11,16 @@ #include "DialogsManagerScriptingInterface.h" +#include + #include "ui/DialogsManager.h" -void DialogsManagerScriptingInterface::toggleAddressBar() { - QMetaObject::invokeMethod(DependencyManager::get().data(), "toggleAddressBar", Qt::QueuedConnection); +DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() { + connect(DependencyManager::get().data(), &DialogsManager::addressBarToggled, + this, &DialogsManagerScriptingInterface::addressBarToggled); +} + +void DialogsManagerScriptingInterface::toggleAddressBar() { + QMetaObject::invokeMethod(DependencyManager::get().data(), + "toggleAddressBar", Qt::QueuedConnection); } diff --git a/interface/src/scripting/DialogsManagerScriptingInterface.h b/interface/src/scripting/DialogsManagerScriptingInterface.h index 9a844f79de..ef44e20d61 100644 --- a/interface/src/scripting/DialogsManagerScriptingInterface.h +++ b/interface/src/scripting/DialogsManagerScriptingInterface.h @@ -16,8 +16,14 @@ class DialogsManagerScriptingInterface : public QObject { Q_OBJECT +public: + DialogsManagerScriptingInterface(); + public slots: void toggleAddressBar(); + +signals: + void addressBarToggled(); }; #endif diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 04f532c59a..ac5e6833fb 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -36,6 +36,7 @@ void DialogsManager::toggleAddressBar() { AddressBarDialog::toggle(); + emit addressBarToggled(); } void DialogsManager::toggleDiskCacheEditor() { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index b15830e35c..09e0274d86 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -72,6 +72,9 @@ public slots: // Application Update void showUpdateDialog(); +signals: + void addressBarToggled(); + private slots: void toggleToolWindow(); void hmdToolsClosed(); From 1d37df47754f24ce87ce735a5900b157d9fa3e22 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 20 Jul 2015 17:49:06 -0700 Subject: [PATCH 085/109] Improve and optimize panel transformations. --- .../src/ui/overlays/BillboardOverlay.cpp | 94 +++++++------------ interface/src/ui/overlays/BillboardOverlay.h | 7 +- interface/src/ui/overlays/PanelAttachable.cpp | 33 ++----- interface/src/ui/overlays/PanelAttachable.h | 18 ++-- 4 files changed, 57 insertions(+), 95 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index c5e73a73e2..d8da58485c 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -39,13 +39,21 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -void BillboardOverlay::update(float deltatime) { - if (getVisible()) { - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); +bool BillboardOverlay::setTransforms(Transform *transform) { + PanelAttachable::setTransforms(transform); + if (_isFacingAvatar) { + glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + setRotation(rotation); + return true; } - } + return false; +// } +// return true; +} + +void BillboardOverlay::update(float deltatime) { + setTransforms(&_transform); } void BillboardOverlay::render(RenderArgs* args) { @@ -58,12 +66,8 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glm::vec3 newPos = getTranslatedPosition(Application::getInstance()->getAvatarPosition()); - if (newPos != glm::vec3()) { - setPosition(newPos); - } - - glm::quat rotation = calculateRotation(args->_viewFrustum->getOrientation()); + Q_ASSERT(args->_batch); + auto batch = args->_batch; float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); @@ -98,24 +102,20 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - auto batch = args->_batch; + setTransforms(&_transform); + Transform transform = _transform; + transform.postScale(glm::vec3(getDimensions(), 1.0f)); - if (batch) { - Transform transform = _transform; - transform.setRotation(rotation); - transform.postScale(glm::vec3(getDimensions(), 1.0f)); - - batch->setModelTransform(transform); - batch->setResourceTexture(0, _texture->getGPUTexture()); - - DependencyManager::get()->bindSimpleProgram(*batch, true, true, true); - DependencyManager::get()->renderQuad( - *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) - ); + batch->setModelTransform(transform); + batch->setResourceTexture(0, _texture->getGPUTexture()); - batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me - } + DependencyManager::get()->bindSimpleProgram(*batch, true, true, false); + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) + ); + + batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me } void BillboardOverlay::setProperties(const QScriptValue &properties) { @@ -207,40 +207,12 @@ void BillboardOverlay::setBillboardURL(const QString& url) { _isLoaded = false; } -glm::quat BillboardOverlay::calculateRotation(glm::quat cameraOrientation) const { - if (_isFacingAvatar) { - // LOL, quaternions are hard. - // glm::vec3 dPos = getPosition() - args->_viewFrustum->getPosition(); - // dPos = glm::normalize(dPos); - // rotation = glm::quat(0, dPos.x, dPos.y, dPos.z); - // float horizontal = glm::sqrt(dPos.x * dPos.x + dPos.y + dPos.y); - // glm::vec3 zAxis = glm::vec3(0, 0, 1); - // rotation = rotationBetween(zAxis, dPos); - // glm::vec3 euler = safeEulerAngles(rotationBetween(zAxis, dPos)); - // rotation = glm::quat(glm::vec3(euler.x, euler.y, 0)); - // float yaw = (dPos.x == 0.0f && dPos.z == 0.0f) ? 0.0f : glm::atan(dPos.x, dPos.z); - // glm::quat yawQuat = glm::quat(glm::vec3(0, yaw, 0)); - // float pitch = (dPos.y == 0.0f && horizontal == 0.0f) ? 0.0f : glm::atan(dPos.y, horizontal); - // glm::quat pitchQuat = glm::quat(glm::vec3(pitch, 0, 0)); - // glm::mat4x4 matrix = glm::lookAt(args->_viewFrustum->getPosition(), getPosition(), - // glm::vec3(0, 1, 0)); - // rotation = glm::quat_cast(matrix); - // rotation = yawQuat * pitchQuat; - // glm::vec3 pitch = glm::vec3(dPos.x, dPos.y, 0); - // rotation = glm::quat(glm::vec3(pitch, yaw, 0)); - // rotate about vertical to be perpendicular to the camera - glm::quat rotation = cameraOrientation; - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - return rotation * getRotation(); - } - return getTranslatedRotation(getRotation()); -} - bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face) { + float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { - glm::quat rotation = - calculateRotation(Application::getInstance()->getCamera()->getRotation()); + // Make sure position and rotation is updated. + setTransforms(&_transform); + // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); @@ -248,7 +220,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float maxSize = glm::max(width, height); glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); - return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance); + return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance); } return false; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 73e9db4c1b..3f8e18a0ef 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -39,10 +39,13 @@ public: virtual BillboardOverlay* createClone() const; +protected: + bool setTransforms(Transform* transform); + private: void setBillboardURL(const QString& url); - glm::quat calculateRotation(glm::quat cameraOrientation) const; - +// glm::quat calculateRotation(glm::quat cameraOrientation) const; + QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 1beea6b74e..7e2ce19099 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -20,31 +20,14 @@ PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : { } -glm::vec3 PanelAttachable::getTranslatedPosition(glm::vec3 avatarPosition) const { +bool PanelAttachable::setTransforms(Transform* transform) { + Q_ASSERT(transform != nullptr); if (getAttachedPanel()) { - glm::vec3 totalOffsetPosition = - getAttachedPanel()->getFacingRotation() * getOffsetPosition() + - getAttachedPanel()->getOffsetPosition(); - - return getAttachedPanel()->getOffsetRotation() * totalOffsetPosition + - avatarPosition; + transform->setTranslation(getAttachedPanel()->getAnchorPosition()); + transform->setRotation(getAttachedPanel()->getOffsetRotation()); + transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); + transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); + return true; } - return glm::vec3(); -} - -glm::quat PanelAttachable::getTranslatedRotation(glm::quat offsetRotation) const { - glm::quat rotation = offsetRotation; - if (getAttachedPanel()) { - rotation *= getAttachedPanel()->getOffsetRotation() * - getAttachedPanel()->getFacingRotation(); - // if (getAttachedPanel()->getFacingRotation() != glm::quat(0, 0, 0, 0)) { - // rotation *= getAttachedPanel()->getFacingRotation(); - // } else if (getAttachedPanel()->getOffsetRotation() != glm::quat(0, 0, 0, 0)) { - // rotation *= getAttachedPanel()->getOffsetRotation(); - // } else { - // rotation *= Application::getInstance()->getCamera()->getOrientation() * - // glm::quat(0, 0, 1, 0); - // } - } - return rotation; + return false; } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index ab8b8a4276..d44504f5d4 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -15,24 +15,28 @@ #include "FloatingUIPanel.h" #include +#include class PanelAttachable { public: PanelAttachable(); PanelAttachable(const PanelAttachable* panelAttachable); - glm::vec3 getOffsetPosition() const { return _offsetPosition; } - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } - void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + glm::vec3 getOffsetPosition() const { return _offsetPosition; } + glm::quat getFacingRotation() const { return _facingRotation; } - glm::vec3 getTranslatedPosition(glm::vec3 avatarPosition) const; - glm::quat getTranslatedRotation(glm::quat offsetRotation) const; + void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + +protected: + bool setTransforms(Transform* transform); private: FloatingUIPanel* _attachedPanel; - glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); + glm::vec3 _offsetPosition; + glm::quat _facingRotation; }; #endif // hifi_PanelAttachable_h From da2afca391054d71328fa8037a1f99706367fb45 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Thu, 23 Jul 2015 08:47:33 -0700 Subject: [PATCH 086/109] Expose overlay type checking to JavaScript. --- .../src/ui/overlays/BillboardOverlay.cpp | 18 +-- interface/src/ui/overlays/BillboardOverlay.h | 6 +- interface/src/ui/overlays/Circle3DOverlay.cpp | 2 + interface/src/ui/overlays/Circle3DOverlay.h | 3 + interface/src/ui/overlays/Cube3DOverlay.cpp | 2 + interface/src/ui/overlays/Cube3DOverlay.h | 3 + interface/src/ui/overlays/FloatingUIPanel.cpp | 153 +++++++++++++++--- interface/src/ui/overlays/FloatingUIPanel.h | 25 ++- interface/src/ui/overlays/Grid3DOverlay.cpp | 3 + interface/src/ui/overlays/Grid3DOverlay.h | 3 + interface/src/ui/overlays/ImageOverlay.cpp | 3 + interface/src/ui/overlays/ImageOverlay.h | 3 + interface/src/ui/overlays/Line3DOverlay.cpp | 3 + interface/src/ui/overlays/Line3DOverlay.h | 3 + .../src/ui/overlays/LocalModelsOverlay.cpp | 3 + .../src/ui/overlays/LocalModelsOverlay.h | 3 + interface/src/ui/overlays/ModelOverlay.cpp | 3 + interface/src/ui/overlays/ModelOverlay.h | 3 + interface/src/ui/overlays/Overlay.cpp | 3 +- interface/src/ui/overlays/Overlay.h | 1 + interface/src/ui/overlays/Overlays.cpp | 95 ++++++----- interface/src/ui/overlays/Overlays.h | 14 +- interface/src/ui/overlays/PanelAttachable.cpp | 12 +- interface/src/ui/overlays/PanelAttachable.h | 10 +- .../src/ui/overlays/Rectangle3DOverlay.cpp | 3 + .../src/ui/overlays/Rectangle3DOverlay.h | 3 + interface/src/ui/overlays/Sphere3DOverlay.cpp | 2 + interface/src/ui/overlays/Sphere3DOverlay.h | 3 + interface/src/ui/overlays/Text3DOverlay.cpp | 2 + interface/src/ui/overlays/Text3DOverlay.h | 3 + interface/src/ui/overlays/TextOverlay.cpp | 2 + interface/src/ui/overlays/TextOverlay.h | 3 + 32 files changed, 294 insertions(+), 104 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index d8da58485c..3e8333b859 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -25,6 +25,8 @@ #include +QString const BillboardOverlay::TYPE = "billboard"; + BillboardOverlay::BillboardOverlay() { _isLoaded = false; } @@ -39,17 +41,13 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -bool BillboardOverlay::setTransforms(Transform *transform) { +void BillboardOverlay::setTransforms(Transform *transform) { PanelAttachable::setTransforms(transform); - if (_isFacingAvatar) { - glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); - rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); - setRotation(rotation); - return true; - } - return false; -// } -// return true; + if (_isFacingAvatar) { + glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); + rotation *= glm::angleAxis(glm::pi(), IDENTITY_UP); + setRotation(rotation); + } } void BillboardOverlay::update(float deltatime) { diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 3f8e18a0ef..47be764f2f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -20,6 +20,9 @@ class BillboardOverlay : public Planar3DOverlay, public PanelAttachable { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + BillboardOverlay(); BillboardOverlay(const BillboardOverlay* billboardOverlay); @@ -40,11 +43,10 @@ public: virtual BillboardOverlay* createClone() const; protected: - bool setTransforms(Transform* transform); + void setTransforms(Transform* transform); private: void setBillboardURL(const QString& url); -// glm::quat calculateRotation(glm::quat cameraOrientation) const; QString _url; NetworkTexturePointer _texture; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 53f1b4ce21..b906f008d6 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -15,6 +15,8 @@ #include +QString const Circle3DOverlay::TYPE = "circle3d"; + Circle3DOverlay::Circle3DOverlay() : _startAt(0.0f), _endAt(360.0f), diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index d83703fcd0..5879fe1701 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -18,6 +18,9 @@ class Circle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Circle3DOverlay(); Circle3DOverlay(const Circle3DOverlay* circle3DOverlay); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 200a1a328f..a306c7c86d 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -19,6 +19,8 @@ #include #include +QString const Cube3DOverlay::TYPE = "cube"; + Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) : Volume3DOverlay(cube3DOverlay) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index 397ad77a9e..6f9026a091 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -17,6 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Cube3DOverlay() {} Cube3DOverlay(const Cube3DOverlay* cube3DOverlay); diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp index e655a75f07..a862e4fcd4 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.cpp +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -13,23 +13,58 @@ #include #include +#include +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" #include "Application.h" +#include "Base3DOverlay.h" -glm::quat FloatingUIPanel::getOffsetRotation() const { - if (getActualOffsetRotation() == glm::quat(0, 0, 0, 0)) { - return Application::getInstance()->getCamera()->getOrientation() * glm::quat(0, 0, 1, 0); - } - return getActualOffsetRotation(); +std::function const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 { + return DependencyManager::get()->getMyAvatar()->getPosition(); +}; + +std::function const FloatingUIPanel::AVATAR_ORIENTATION = []() -> glm::quat { + return DependencyManager::get()->getMyAvatar()->getOrientation() * + glm::angleAxis(glm::pi(), IDENTITY_UP); +}; + +FloatingUIPanel::FloatingUIPanel() : + _anchorPosition(AVATAR_POSITION), + _offsetRotation(AVATAR_ORIENTATION) +{ +} + +glm::vec3 FloatingUIPanel::getPosition() const { + return getOffsetRotation() * getOffsetPosition() + getAnchorPosition(); +} + +glm::quat FloatingUIPanel::getRotation() const { + return getOffsetRotation() * getFacingRotation(); +} + +void FloatingUIPanel::setAnchorPosition(glm::vec3 position) { + setAnchorPosition([position]() -> glm::vec3 { + return position; + }); +} + +void FloatingUIPanel::setOffsetRotation(glm::quat rotation) { + setOffsetRotation([rotation]() -> glm::quat { + return rotation; + }); } QScriptValue FloatingUIPanel::getProperty(const QString &property) { - if (property == "offsetPosition") { - return vec3toScriptValue(_scriptEngine, getOffsetPosition()); + if (property == "anchorPosition") { + return vec3toScriptValue(_scriptEngine, getAnchorPosition()); } if (property == "offsetRotation") { - return quatToScriptValue(_scriptEngine, getActualOffsetRotation()); + return quatToScriptValue(_scriptEngine, getOffsetRotation()); + } + if (property == "offsetPosition") { + return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } if (property == "facingRotation") { return quatToScriptValue(_scriptEngine, getFacingRotation()); @@ -39,6 +74,91 @@ QScriptValue FloatingUIPanel::getProperty(const QString &property) { } void FloatingUIPanel::setProperties(const QScriptValue &properties) { + QScriptValue anchor = properties.property("anchorPosition"); + if (anchor.isValid()) { + QScriptValue type = anchor.property("type"); + QScriptValue value = anchor.property("value"); + + if (type.isValid()) { + QString typeString = type.toVariant().toString(); + if (typeString == "myAvatar") { + setAnchorPosition(AVATAR_POSITION); + } else if (value.isValid()) { + if (typeString == "overlay") { + Overlay::Pointer overlay = Application::getInstance()->getOverlays() + .getOverlay(value.toVariant().toUInt()); + if (overlay->is3D()) { + auto overlay3D = std::static_pointer_cast(overlay); + setAnchorPosition([&overlay3D]() -> glm::vec3 { + return overlay3D->getPosition(); + }); + } + } else if (typeString == "panel") { + FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() + .getPanel(value.toVariant().toUInt()); + setAnchorPosition([panel]() -> glm::vec3 { + return panel->getPosition(); + }); + } else if (typeString == "vec3") { + QScriptValue x = value.property("x"); + QScriptValue y = value.property("y"); + QScriptValue z = value.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setAnchorPosition(newPosition); + } + } + } + } + } + + QScriptValue offsetRotation = properties.property("offsetRotation"); + if (offsetRotation.isValid()) { + QScriptValue type = offsetRotation.property("type"); + QScriptValue value = offsetRotation.property("value"); + + if (type.isValid()) { + QString typeString = type.toVariant().toString(); + if (typeString == "myAvatar") { + setOffsetRotation(AVATAR_ORIENTATION); + } else if (value.isValid()) { + if (typeString == "overlay") { + Overlay::Pointer overlay = Application::getInstance()->getOverlays() + .getOverlay(value.toVariant().toUInt()); + if (overlay->is3D()) { + auto overlay3D = std::static_pointer_cast(overlay); + setOffsetRotation([&overlay3D]() -> glm::quat { + return overlay3D->getRotation(); + }); + } + } else if (typeString == "panel") { + FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() + .getPanel(value.toVariant().toUInt()); + setOffsetRotation([panel]() -> glm::quat { + return panel->getRotation(); + }); + } else if (typeString == "quat") { + QScriptValue x = value.property("x"); + QScriptValue y = value.property("y"); + QScriptValue z = value.property("z"); + QScriptValue w = value.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setOffsetRotation(newRotation); + } + } + } + } + } + QScriptValue offsetPosition = properties.property("offsetPosition"); if (offsetPosition.isValid()) { QScriptValue x = offsetPosition.property("x"); @@ -53,23 +173,6 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { } } - QScriptValue offsetRotation = properties.property("offsetRotation"); - if (offsetRotation.isValid()) { - QScriptValue x = offsetRotation.property("x"); - QScriptValue y = offsetRotation.property("y"); - QScriptValue z = offsetRotation.property("z"); - QScriptValue w = offsetRotation.property("w"); - - if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { - glm::quat newRotation; - newRotation.x = x.toVariant().toFloat(); - newRotation.y = y.toVariant().toFloat(); - newRotation.z = z.toVariant().toFloat(); - newRotation.w = w.toVariant().toFloat(); - setOffsetRotation(newRotation); - } - } - QScriptValue facingRotation = properties.property("facingRotation"); if (offsetRotation.isValid()) { QScriptValue x = facingRotation.property("x"); diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index 7f8d42eb5b..abc1328928 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -25,22 +25,31 @@ public: void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + glm::vec3 getAnchorPosition() const { return _anchorPosition(); } + glm::quat getOffsetRotation() const { return _offsetRotation(); } glm::vec3 getOffsetPosition() const { return _offsetPosition; } - glm::quat getOffsetRotation() const; - glm::quat getActualOffsetRotation() const { return _offsetRotation; } glm::quat getFacingRotation() const { return _facingRotation; } + glm::vec3 getPosition() const; + glm::quat getRotation() const; - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; }; - void setOffsetRotation(glm::quat rotation) { _offsetRotation = rotation; }; - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; }; + void setAnchorPosition(const std::function& func) { _anchorPosition = func; } + void setAnchorPosition(glm::vec3 position); + void setOffsetRotation(const std::function& func) { _offsetRotation = func; } + void setOffsetRotation(glm::quat rotation); + void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } + void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } QScriptValue getProperty(const QString& property); void setProperties(const QScriptValue& properties); private: - glm::vec3 _offsetPosition = glm::vec3(0, 0, 0); - glm::quat _offsetRotation = glm::quat(0, 0, 0, 0); - glm::quat _facingRotation = glm::quat(1, 0, 0, 0); + static std::function const AVATAR_POSITION; + static std::function const AVATAR_ORIENTATION; + + std::function _anchorPosition; + std::function _offsetRotation; + glm::vec3 _offsetPosition{0, 0, 0}; + glm::quat _facingRotation{1, 0, 0, 0}; QScriptEngine* _scriptEngine; }; diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 956eae35ff..074ad3d17f 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -18,6 +18,9 @@ #include #include + +QString const Grid3DOverlay::TYPE = "grid"; + Grid3DOverlay::Grid3DOverlay() : _minorGridWidth(1.0), _majorGridEvery(5) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index 80ec4b84b9..5fb3852905 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -18,6 +18,9 @@ class Grid3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Grid3DOverlay(); Grid3DOverlay(const Grid3DOverlay* grid3DOverlay); diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index cad9010531..ed88f50981 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -16,6 +16,9 @@ #include #include + +QString const ImageOverlay::TYPE = "image"; + ImageOverlay::ImageOverlay() : _imageURL(), _renderImage(false), diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 59d4102933..5698a73732 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -24,6 +24,9 @@ class ImageOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ImageOverlay(); ImageOverlay(const ImageOverlay* imageOverlay); diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d026e5f56e..0acd7ecc1e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Line3DOverlay::TYPE = "line3d"; + Line3DOverlay::Line3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index 4a4d8f4d90..05812709dc 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -17,6 +17,9 @@ class Line3DOverlay : public Base3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Line3DOverlay(); Line3DOverlay(const Line3DOverlay* line3DOverlay); ~Line3DOverlay(); diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp index 3f701cbbe6..38e11562da 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp @@ -14,6 +14,9 @@ #include #include + +QString const LocalModelsOverlay::TYPE = "localmodels"; + LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) : Volume3DOverlay(), _entityTreeRenderer(entityTreeRenderer) { diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h index c311b2bc1b..011f3dfb11 100644 --- a/interface/src/ui/overlays/LocalModelsOverlay.h +++ b/interface/src/ui/overlays/LocalModelsOverlay.h @@ -19,6 +19,9 @@ class EntityTreeRenderer; class LocalModelsOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer); LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ed15e57d43..7559d696d5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -13,6 +13,9 @@ #include "Application.h" + +QString const ModelOverlay::TYPE = "model"; + ModelOverlay::ModelOverlay() : _model(), _modelTextures(QVariantMap()), diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 1c43f42909..97ecfb642c 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -19,6 +19,9 @@ class ModelOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + ModelOverlay(); ModelOverlay(const ModelOverlay* modelOverlay); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 17e4ae74e1..7824c0c498 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -110,7 +110,8 @@ void Overlay::setProperties(const QScriptValue& properties) { } if (properties.property("visible").isValid()) { - setVisible(properties.property("visible").toVariant().toBool()); + bool visible = properties.property("visible").toVariant().toBool(); + setVisible(visible); } if (properties.property("anchor").isValid()) { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1698d71fc3..9a19c71db6 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -44,6 +44,7 @@ public: virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr scene, render::PendingChanges& pendingChanges); // getters + virtual QString getType() const = 0; virtual bool is3D() const = 0; bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fe489847ee..fb9f64c84e 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -135,13 +135,13 @@ Overlay::Pointer Overlays::getOverlay(unsigned int id) const { return nullptr; } -void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property) { - if (PanelAttachable* attachable = dynamic_cast(overlay)) { +void Overlays::setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property) { + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { if (property.isValid()) { unsigned int attachedPanelId = property.toVariant().toUInt(); - FloatingUIPanel* panel = nullptr; if (_panels.contains(attachedPanelId)) { - panel = _panels[attachedPanelId].get(); + auto panel = _panels[attachedPanelId]; panel->children.append(overlayId); attachable->setAttachedPanel(panel); } else { @@ -153,32 +153,32 @@ void Overlays::setAttachedPanel(Overlay* overlay, unsigned int overlayId, const } unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { - Overlay* thisOverlay = NULL; + Overlay::Pointer thisOverlay = nullptr; - if (type == "image") { - thisOverlay = new ImageOverlay(); - } else if (type == "text") { - thisOverlay = new TextOverlay(); - } else if (type == "text3d") { - thisOverlay = new Text3DOverlay(); - } else if (type == "cube") { - thisOverlay = new Cube3DOverlay(); - } else if (type == "sphere") { - thisOverlay = new Sphere3DOverlay(); - } else if (type == "circle3d") { - thisOverlay = new Circle3DOverlay(); - } else if (type == "rectangle3d") { - thisOverlay = new Rectangle3DOverlay(); - } else if (type == "line3d") { - thisOverlay = new Line3DOverlay(); - } else if (type == "grid") { - thisOverlay = new Grid3DOverlay(); - } else if (type == "localmodels") { - thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer()); - } else if (type == "model") { - thisOverlay = new ModelOverlay(); - } else if (type == "billboard") { - thisOverlay = new BillboardOverlay(); + if (type == ImageOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == TextOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Text3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Cube3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Sphere3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Circle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Rectangle3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Line3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == Grid3DOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == LocalModelsOverlay::TYPE) { + thisOverlay = std::make_shared(Application::getInstance()->getEntityClipboardRenderer()); + } else if (type == ModelOverlay::TYPE) { + thisOverlay = std::make_shared(); + } else if (type == BillboardOverlay::TYPE) { + thisOverlay = std::make_shared(); } if (thisOverlay) { @@ -190,29 +190,28 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope return 0; } -unsigned int Overlays::addOverlay(Overlay* overlay) { - Overlay::Pointer overlayPointer(overlay); +unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { overlay->init(_scriptEngine); QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; _nextOverlayID++; if (overlay->is3D()) { - Base3DOverlay* overlay3D = static_cast(overlay); + auto overlay3D = std::static_pointer_cast(overlay); if (overlay3D->getDrawOnHUD()) { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } else { - _overlaysWorld[thisID] = overlayPointer; + _overlaysWorld[thisID] = overlay; render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; - overlayPointer->addToScene(overlayPointer, scene, pendingChanges); + overlay->addToScene(overlay, scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); } } else { - _overlaysHUD[thisID] = overlayPointer; + _overlaysHUD[thisID] = overlay; } return thisID; @@ -222,7 +221,7 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { - return addOverlay(thisOverlay->createClone()); + return addOverlay(Overlay::Pointer(thisOverlay->createClone())); } return 0; // Not found @@ -253,7 +252,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } - setAttachedPanel(thisOverlay.get(), id, properties.property("attachedPanel")); + setAttachedPanel(thisOverlay, id, properties.property("attachedPanel")); return true; } @@ -278,6 +277,14 @@ void Overlays::deleteOverlay(unsigned int id) { _overlaysToDelete.push_back(overlayToDelete); } +QString Overlays::getOverlayType(unsigned int overlayId) const { + Overlay::Pointer overlay = getOverlay(overlayId); + if (overlay) { + return overlay->getType(); + } + return ""; +} + unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { @@ -322,8 +329,9 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop QReadLocker lock(&_lock); if (thisOverlay) { if (property == "attachedPanel") { - if (FloatingUIPanel* panel = dynamic_cast(thisOverlay.get())) { - result.value = _panels.key(FloatingUIPanel::Pointer(panel)); + auto panelAttachable = std::dynamic_pointer_cast(thisOverlay); + if (panelAttachable) { + result.value = _panels.key(panelAttachable->getAttachedPanel()); } else { result.value = 0; } @@ -499,19 +507,18 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const { return QSizeF(0.0f, 0.0f); } -unsigned int Overlays::addPanel(FloatingUIPanel* panel) { +unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) { QWriteLocker lock(&_lock); - FloatingUIPanel::Pointer panelPointer(panel); unsigned int thisID = _nextOverlayID; _nextOverlayID++; - _panels[thisID] = panelPointer; + _panels[thisID] = panel; return thisID; } unsigned int Overlays::addPanel(const QScriptValue& properties) { - FloatingUIPanel* panel = new FloatingUIPanel(); + FloatingUIPanel::Pointer panel = std::make_shared(); panel->init(_scriptEngine); panel->setProperties(properties); return addPanel(panel); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 80da2e6db1..ee4fa5b7d4 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -66,12 +66,16 @@ public: void update(float deltatime); void renderHUD(RenderArgs* renderArgs); + Overlay::Pointer getOverlay(unsigned int id) const; + FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } + public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QString& type, const QScriptValue& properties); /// adds an overlay that's already been created - unsigned int addOverlay(Overlay* overlay); + unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } + unsigned int addOverlay(Overlay::Pointer overlay); /// clones an existing overlay unsigned int cloneOverlay(unsigned int id); @@ -83,6 +87,9 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); + /// + QString getOverlayType(unsigned int overlayId) const; + /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); @@ -101,7 +108,7 @@ public slots: /// adds a panel that has already been created - unsigned int addPanel(FloatingUIPanel* panel); + unsigned int addPanel(FloatingUIPanel::Pointer panel); /// creates and adds a panel based on a set of properties unsigned int addPanel(const QScriptValue& properties); @@ -117,8 +124,7 @@ public slots: private: void cleanupOverlaysToDelete(); - Overlay::Pointer getOverlay(unsigned int id) const; - void setAttachedPanel(Overlay* overlay, unsigned int overlayId, const QScriptValue& property); + void setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property); QMap _overlaysHUD; QMap _overlaysWorld; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index 7e2ce19099..bedf7d9fd5 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -3,31 +3,33 @@ // hifi // // Created by Zander Otavka on 7/15/15. +// 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 "PanelAttachable.h" PanelAttachable::PanelAttachable() : _attachedPanel(nullptr), - _offsetPosition(glm::vec3()) + _facingRotation(1, 0, 0, 0) { } PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : _attachedPanel(panelAttachable->_attachedPanel), - _offsetPosition(panelAttachable->_offsetPosition) + _offsetPosition(panelAttachable->_offsetPosition), + _facingRotation(panelAttachable->_facingRotation) { } -bool PanelAttachable::setTransforms(Transform* transform) { +void PanelAttachable::setTransforms(Transform* transform) { Q_ASSERT(transform != nullptr); if (getAttachedPanel()) { transform->setTranslation(getAttachedPanel()->getAnchorPosition()); transform->setRotation(getAttachedPanel()->getOffsetRotation()); transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); - return true; } - return false; } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index d44504f5d4..097fc9a517 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -3,7 +3,7 @@ // interface/src/ui/overlays // // Created by Zander Otavka on 7/1/15. -// Copyright 2014 High Fidelity, Inc. +// 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 @@ -22,19 +22,19 @@ public: PanelAttachable(); PanelAttachable(const PanelAttachable* panelAttachable); - FloatingUIPanel* getAttachedPanel() const { return _attachedPanel; } + FloatingUIPanel::Pointer getAttachedPanel() const { return _attachedPanel; } glm::vec3 getOffsetPosition() const { return _offsetPosition; } glm::quat getFacingRotation() const { return _facingRotation; } - void setAttachedPanel(FloatingUIPanel* panel) { _attachedPanel = panel; } + void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; } void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } protected: - bool setTransforms(Transform* transform); + void setTransforms(Transform* transform); private: - FloatingUIPanel* _attachedPanel; + FloatingUIPanel::Pointer _attachedPanel; glm::vec3 _offsetPosition; glm::quat _facingRotation; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 1d5183b833..64c5e4c819 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -13,6 +13,9 @@ #include #include + +QString const Rectangle3DOverlay::TYPE = "rectangle3d"; + Rectangle3DOverlay::Rectangle3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h index f59ab21198..e5eba8bce8 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ b/interface/src/ui/overlays/Rectangle3DOverlay.h @@ -17,6 +17,9 @@ class Rectangle3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Rectangle3DOverlay(); Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay); ~Rectangle3DOverlay(); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 307b60b2ce..9712375209 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -15,6 +15,8 @@ #include #include +QString const Sphere3DOverlay::TYPE = "sphere"; + Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : Volume3DOverlay(Sphere3DOverlay) { diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index b82dc548f1..d371077502 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -17,6 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Sphere3DOverlay() {} Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 737b2d0bc5..00a7d75090 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -23,6 +23,8 @@ const int FIXED_FONT_POINT_SIZE = 40; const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation const float LINE_SCALE_RATIO = 1.2f; +QString const Text3DOverlay::TYPE = "text3d"; + Text3DOverlay::Text3DOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 666b43d8b1..abd5ef54bd 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -21,6 +21,9 @@ class Text3DOverlay : public Planar3DOverlay { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + Text3DOverlay(); Text3DOverlay(const Text3DOverlay* text3DOverlay); ~Text3DOverlay(); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index ea0c4f6026..e9fda2def8 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -78,6 +78,8 @@ QString toQmlColor(const glm::vec4& v) { arg((int)(v.b * 255), 2, 16, QChar('0')); } +QString const TextOverlay::TYPE = "text"; + TextOverlay::TextOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index b70b95ca3b..a8e6967871 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -29,6 +29,9 @@ class TextOverlay : public Overlay2D { Q_OBJECT public: + static QString const TYPE; + virtual QString getType() const { return TYPE; } + TextOverlay(); TextOverlay(const TextOverlay* textOverlay); ~TextOverlay(); From bc5ef8eb113f32295a7a12e1d8fc1b5096e49658 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 17:50:17 -0700 Subject: [PATCH 087/109] Clean up C++ relating to panels. --- .../src/ui/overlays/BillboardOverlay.cpp | 28 ++----- interface/src/ui/overlays/FloatingUIPanel.cpp | 53 +++++++------ interface/src/ui/overlays/FloatingUIPanel.h | 22 ++++-- interface/src/ui/overlays/Overlays.cpp | 75 ++++++++++--------- interface/src/ui/overlays/Overlays.h | 10 ++- interface/src/ui/overlays/PanelAttachable.cpp | 46 ++++++++++++ interface/src/ui/overlays/PanelAttachable.h | 7 +- 7 files changed, 151 insertions(+), 90 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3e8333b859..8ffa909e12 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -13,17 +13,15 @@ #include +#include #include #include #include +#include #include "Application.h" #include "GeometryUtil.h" -#include "DeferredLightingEffect.h" - -#include - QString const BillboardOverlay::TYPE = "billboard"; @@ -107,7 +105,7 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - DependencyManager::get()->bindSimpleProgram(*batch, true, true, false); + DependencyManager::get()->bindSimpleProgram(*batch, true, true, false, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) @@ -118,6 +116,7 @@ void BillboardOverlay::render(RenderArgs* args) { void BillboardOverlay::setProperties(const QScriptValue &properties) { Planar3DOverlay::setProperties(properties); + PanelAttachable::setProperties(properties); QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { @@ -162,21 +161,6 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { if (isFacingAvatarValue.isValid()) { _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); } - - QScriptValue offsetPosition = properties.property("offsetPosition"); - if (offsetPosition.isValid()) { - QScriptValue x = offsetPosition.property("x"); - QScriptValue y = offsetPosition.property("y"); - QScriptValue z = offsetPosition.property("z"); - - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setOffsetPosition(newPosition); - } - } } QScriptValue BillboardOverlay::getProperty(const QString& property) { @@ -193,6 +177,10 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) { return vec3toScriptValue(_scriptEngine, getOffsetPosition()); } + QScriptValue value = PanelAttachable::getProperty(_scriptEngine, property); + if (value.isValid()) { + return value; + } return Planar3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/FloatingUIPanel.cpp b/interface/src/ui/overlays/FloatingUIPanel.cpp index a862e4fcd4..eedb4bffd1 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.cpp +++ b/interface/src/ui/overlays/FloatingUIPanel.cpp @@ -20,7 +20,6 @@ #include "Application.h" #include "Base3DOverlay.h" - std::function const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 { return DependencyManager::get()->getMyAvatar()->getPosition(); }; @@ -30,12 +29,6 @@ std::function const FloatingUIPanel::AVATAR_ORIENTATION = []() -> g glm::angleAxis(glm::pi(), IDENTITY_UP); }; -FloatingUIPanel::FloatingUIPanel() : - _anchorPosition(AVATAR_POSITION), - _offsetRotation(AVATAR_ORIENTATION) -{ -} - glm::vec3 FloatingUIPanel::getPosition() const { return getOffsetRotation() * getOffsetPosition() + getAnchorPosition(); } @@ -44,18 +37,30 @@ glm::quat FloatingUIPanel::getRotation() const { return getOffsetRotation() * getFacingRotation(); } -void FloatingUIPanel::setAnchorPosition(glm::vec3 position) { +void FloatingUIPanel::setAnchorPosition(const glm::vec3& position) { setAnchorPosition([position]() -> glm::vec3 { return position; }); } -void FloatingUIPanel::setOffsetRotation(glm::quat rotation) { +void FloatingUIPanel::setOffsetRotation(const glm::quat& rotation) { setOffsetRotation([rotation]() -> glm::quat { return rotation; }); } +void FloatingUIPanel::addChild(unsigned int childId) { + if (!_children.contains(childId)) { + _children.append(childId); + } +} + +void FloatingUIPanel::removeChild(unsigned int childId) { + if (_children.contains(childId)) { + _children.removeOne(childId); + } +} + QScriptValue FloatingUIPanel::getProperty(const QString &property) { if (property == "anchorPosition") { return vec3toScriptValue(_scriptEngine, getAnchorPosition()); @@ -76,15 +81,15 @@ QScriptValue FloatingUIPanel::getProperty(const QString &property) { void FloatingUIPanel::setProperties(const QScriptValue &properties) { QScriptValue anchor = properties.property("anchorPosition"); if (anchor.isValid()) { - QScriptValue type = anchor.property("type"); + QScriptValue bindType = anchor.property("bind"); QScriptValue value = anchor.property("value"); - if (type.isValid()) { - QString typeString = type.toVariant().toString(); - if (typeString == "myAvatar") { + if (bindType.isValid()) { + QString bindTypeString = bindType.toVariant().toString(); + if (bindTypeString == "myAvatar") { setAnchorPosition(AVATAR_POSITION); } else if (value.isValid()) { - if (typeString == "overlay") { + if (bindTypeString == "overlay") { Overlay::Pointer overlay = Application::getInstance()->getOverlays() .getOverlay(value.toVariant().toUInt()); if (overlay->is3D()) { @@ -93,13 +98,13 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { return overlay3D->getPosition(); }); } - } else if (typeString == "panel") { + } else if (bindTypeString == "panel") { FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() .getPanel(value.toVariant().toUInt()); setAnchorPosition([panel]() -> glm::vec3 { return panel->getPosition(); }); - } else if (typeString == "vec3") { + } else if (bindTypeString == "vec3") { QScriptValue x = value.property("x"); QScriptValue y = value.property("y"); QScriptValue z = value.property("z"); @@ -117,15 +122,15 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { QScriptValue offsetRotation = properties.property("offsetRotation"); if (offsetRotation.isValid()) { - QScriptValue type = offsetRotation.property("type"); + QScriptValue bindType = offsetRotation.property("bind"); QScriptValue value = offsetRotation.property("value"); - if (type.isValid()) { - QString typeString = type.toVariant().toString(); - if (typeString == "myAvatar") { + if (bindType.isValid()) { + QString bindTypeString = bindType.toVariant().toString(); + if (bindTypeString == "myAvatar") { setOffsetRotation(AVATAR_ORIENTATION); } else if (value.isValid()) { - if (typeString == "overlay") { + if (bindTypeString == "overlay") { Overlay::Pointer overlay = Application::getInstance()->getOverlays() .getOverlay(value.toVariant().toUInt()); if (overlay->is3D()) { @@ -134,13 +139,13 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { return overlay3D->getRotation(); }); } - } else if (typeString == "panel") { + } else if (bindTypeString == "panel") { FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays() .getPanel(value.toVariant().toUInt()); setOffsetRotation([panel]() -> glm::quat { return panel->getRotation(); }); - } else if (typeString == "quat") { + } else if (bindTypeString == "quat") { QScriptValue x = value.property("x"); QScriptValue y = value.property("y"); QScriptValue z = value.property("z"); @@ -174,7 +179,7 @@ void FloatingUIPanel::setProperties(const QScriptValue &properties) { } QScriptValue facingRotation = properties.property("facingRotation"); - if (offsetRotation.isValid()) { + if (facingRotation.isValid()) { QScriptValue x = facingRotation.property("x"); QScriptValue y = facingRotation.property("y"); QScriptValue z = facingRotation.property("z"); diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index abc1328928..f65fa93c26 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -12,6 +12,8 @@ #ifndef hifi_FloatingUIPanel_h #define hifi_FloatingUIPanel_h +#include + #include #include #include @@ -21,8 +23,6 @@ class FloatingUIPanel : public QObject { public: typedef std::shared_ptr Pointer; - QList children; - void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } glm::vec3 getAnchorPosition() const { return _anchorPosition(); } @@ -33,11 +33,16 @@ public: glm::quat getRotation() const; void setAnchorPosition(const std::function& func) { _anchorPosition = func; } - void setAnchorPosition(glm::vec3 position); + void setAnchorPosition(const glm::vec3& position); void setOffsetRotation(const std::function& func) { _offsetRotation = func; } - void setOffsetRotation(glm::quat rotation); - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + void setOffsetRotation(const glm::quat& rotation); + void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } + void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } + + const QList& getChildren() { return _children; } + void addChild(unsigned int childId); + void removeChild(unsigned int childId); + unsigned int popLastChild() { return _children.takeLast(); } QScriptValue getProperty(const QString& property); void setProperties(const QScriptValue& properties); @@ -46,11 +51,12 @@ private: static std::function const AVATAR_POSITION; static std::function const AVATAR_ORIENTATION; - std::function _anchorPosition; - std::function _offsetRotation; + std::function _anchorPosition{AVATAR_POSITION}; + std::function _offsetRotation{AVATAR_ORIENTATION}; glm::vec3 _offsetPosition{0, 0, 0}; glm::quat _facingRotation{1, 0, 0, 0}; QScriptEngine* _scriptEngine; + QList _children; }; #endif // hifi_FloatingUIPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fb9f64c84e..bce219b4b4 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -135,23 +135,6 @@ Overlay::Pointer Overlays::getOverlay(unsigned int id) const { return nullptr; } -void Overlays::setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property) { - auto attachable = std::dynamic_pointer_cast(overlay); - if (attachable) { - if (property.isValid()) { - unsigned int attachedPanelId = property.toVariant().toUInt(); - if (_panels.contains(attachedPanelId)) { - auto panel = _panels[attachedPanelId]; - panel->children.append(overlayId); - attachable->setAttachedPanel(panel); - } else { - attachable->getAttachedPanel()->children.removeAll(overlayId); - attachable->setAttachedPanel(nullptr); - } - } - } -} - unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { Overlay::Pointer thisOverlay = nullptr; @@ -183,9 +166,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope if (thisOverlay) { thisOverlay->setProperties(properties); - unsigned int overlayId = addOverlay(thisOverlay); - setAttachedPanel(thisOverlay, overlayId, properties.property("attachedPanel")); - return overlayId; + return addOverlay(thisOverlay); } return 0; } @@ -252,8 +233,6 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { thisOverlay->setProperties(properties); } - setAttachedPanel(thisOverlay, id, properties.property("attachedPanel")); - return true; } return false; @@ -273,8 +252,16 @@ void Overlays::deleteOverlay(unsigned int id) { } } + auto attachable = std::dynamic_pointer_cast(overlayToDelete); + if (attachable && attachable->getAttachedPanel()) { + attachable->getAttachedPanel()->removeChild(id); + attachable->setAttachedPanel(nullptr); + } + QWriteLocker lock(&_deleteLock); _overlaysToDelete.push_back(overlayToDelete); + + emit overlayDeleted(id); } QString Overlays::getOverlayType(unsigned int overlayId) const { @@ -285,6 +272,33 @@ QString Overlays::getOverlayType(unsigned int overlayId) const { return ""; } +unsigned int Overlays::getAttachedPanel(unsigned int childId) const { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + return _panels.key(attachable->getAttachedPanel()); + } + return 0; +} + +void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) { + Overlay::Pointer overlay = getOverlay(childId); + auto attachable = std::dynamic_pointer_cast(overlay); + if (attachable) { + if (_panels.contains(panelId)) { + auto panel = _panels[panelId]; + panel->addChild(childId); + attachable->setAttachedPanel(panel); + } else { + auto panel = attachable->getAttachedPanel(); + if (panel) { + panel->removeChild(childId); + attachable->setAttachedPanel(nullptr); + } + } + } +} + unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { glm::vec2 pointCopy = point; if (qApp->isHMDMode()) { @@ -328,16 +342,7 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop Overlay::Pointer thisOverlay = getOverlay(id); QReadLocker lock(&_lock); if (thisOverlay) { - if (property == "attachedPanel") { - auto panelAttachable = std::dynamic_pointer_cast(thisOverlay); - if (panelAttachable) { - result.value = _panels.key(panelAttachable->getAttachedPanel()); - } else { - result.value = 0; - } - } else { - result.value = thisOverlay->getProperty(property); - } + result.value = thisOverlay->getProperty(property); } return result; } @@ -553,7 +558,9 @@ void Overlays::deletePanel(unsigned int panelId) { } } - while (!panelToDelete->children.isEmpty()) { - deleteOverlay(panelToDelete->children.takeLast()); + while (!panelToDelete->getChildren().isEmpty()) { + deleteOverlay(panelToDelete->popLastChild()); } + + emit panelDeleted(panelId); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index ee4fa5b7d4..ce2c3efeae 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -87,9 +87,12 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); - /// + /// get the string type of the overlay used in addOverlay QString getOverlayType(unsigned int overlayId) const; + unsigned int getAttachedPanel(unsigned int childId) const; + void setAttachedPanel(unsigned int childId, unsigned int panelId); + /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); @@ -122,9 +125,12 @@ public slots: /// deletes a panel and all child overlays void deletePanel(unsigned int panelId); +signals: + void overlayDeleted(unsigned int id); + void panelDeleted(unsigned int id); + private: void cleanupOverlaysToDelete(); - void setAttachedPanel(Overlay::Pointer overlay, unsigned int overlayId, const QScriptValue& property); QMap _overlaysHUD; QMap _overlaysWorld; diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index bedf7d9fd5..e114fa143e 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -11,6 +11,8 @@ #include "PanelAttachable.h" +#include + PanelAttachable::PanelAttachable() : _attachedPanel(nullptr), _facingRotation(1, 0, 0, 0) @@ -33,3 +35,47 @@ void PanelAttachable::setTransforms(Transform* transform) { transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); } } + +QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) { + if (property == "offsetPosition") { + return vec3toScriptValue(scriptEngine, getOffsetPosition()); + } + if (property == "facingRotation") { + return quatToScriptValue(scriptEngine, getFacingRotation()); + } + return QScriptValue(); +} + +void PanelAttachable::setProperties(const QScriptValue &properties) { + QScriptValue offsetPosition = properties.property("offsetPosition"); + if (offsetPosition.isValid()) { + QScriptValue x = offsetPosition.property("x"); + QScriptValue y = offsetPosition.property("y"); + QScriptValue z = offsetPosition.property("z"); + + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setOffsetPosition(newPosition); + } + } + + QScriptValue facingRotation = properties.property("facingRotation"); + if (facingRotation.isValid()) { + QScriptValue x = facingRotation.property("x"); + QScriptValue y = facingRotation.property("y"); + QScriptValue z = facingRotation.property("z"); + QScriptValue w = facingRotation.property("w"); + + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + setFacingRotation(newRotation); + } + } +} diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 097fc9a517..e1fc490d00 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -27,8 +27,11 @@ public: glm::quat getFacingRotation() const { return _facingRotation; } void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; } - void setOffsetPosition(glm::vec3 position) { _offsetPosition = position; } - void setFacingRotation(glm::quat rotation) { _facingRotation = rotation; } + void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } + void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; } + + QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); + void setProperties(const QScriptValue& properties); protected: void setTransforms(Transform* transform); From ba190b4b3453a3908a73ad36b3d51640d4386ead Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:00:33 -0700 Subject: [PATCH 088/109] Improve JavaScript abstraction layer for overlays. Also move it to it's own file. --- examples/libraries/overlayManager.js | 451 +++++++++++++++++++++++++++ examples/libraries/overlayUtils.js | 259 +-------------- 2 files changed, 459 insertions(+), 251 deletions(-) create mode 100644 examples/libraries/overlayManager.js diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js new file mode 100644 index 0000000000..55575badee --- /dev/null +++ b/examples/libraries/overlayManager.js @@ -0,0 +1,451 @@ +// +// overlayManager.js +// examples/libraries +// +// Created by Zander Otavka on 7/24/15 +// Copyright 2015 High Fidelity, Inc. +// +// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. +// Instead of: +// +// var billboard = Overlays.addOverlay("billboard", { visible: false }); +// ... +// Overlays.editOverlay(billboard, { visible: true }); +// ... +// Overlays.deleteOverlay(billboard); +// +// You can now do: +// +// var billboard = new BillboardOverlay({ visible: false }); +// ... +// billboard.visible = true; +// ... +// billboard.destroy(); +// +// See more on usage below. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +(function() { + // Delete `Overlays` from the global scope. + var Overlays = this.Overlays; + delete this.Overlays; + + var overlays = {}; + var panels = {}; + + var overlayTypes; + var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay; + + + // + // Create a new JavaScript object for an overlay of given ID. + // + function makeOverlayFromId(id) { + var type = Overlays.getOverlayType(id); + if (!type) { + return null; + } + var overlay = new overlayTypes[type](); + overlay._id = id; + var panelID = Overlays.getAttachedPanel(id) + if (panelID && panelID in panels) { + panels[panelID].addChild(overlay); + } + overlays[id] = overlay; + return overlay; + } + + // + // Get or create an overlay object from the id. + // + // @param knownOverlaysOnly (Optional: Boolean) + // If true, a new object will not be created. + // @param searchList (Optional: Object) + // Map of overlay id's and overlay objects. Can be generated with + // `OverlayManager.makeSearchList`. + // + function findOverlay(id, knownOverlaysOnly, searchList) { + if (id > 0) { + knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList); + searchList = searchList || overlays; + var foundOverlay = searchList[id]; + if (foundOverlay) { + return foundOverlay; + } + if (!knownOverlaysOnly) { + return makeOverlayFromId(id); + } + } + return null; + } + + + // + // Perform global scoped operations on overlays, such as finding by ray intersection. + // + OverlayManager = { + findOnRay: function(pickRay, knownOverlaysOnly, searchList) { + var rayPickResult = Overlays.findRayIntersection(pickRay); + print("raypick " + rayPickResult.overlayID); + if (rayPickResult.intersects) { + return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList); + } + return null; + }, + findAtPoint: function(point, knownOverlaysOnly, searchList) { + var foundID = Overlays.getOverlayAtPoint(point); + print("at point " + foundID); + if (foundID) { + return findOverlay(foundID, knownOverlaysOnly, searchList); + } else { + var pickRay = Camera.computePickRay(point.x, point.y); + return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList); + } + }, + makeSearchList: function(overlayArray) { + var searchList = {}; + overlayArray.forEach(function(overlay){ + searchList[overlay._id] = overlay; + }); + return searchList; + } + }; + + + // + // Object oriented abstraction layer for overlays. + // + // Usage: + // // Create an overlay + // var billboard = new BillboardOverlay({ + // visible: true, + // isFacingAvatar: true, + // ignoreRayIntersections: false + // }); + // + // // Get a property + // var isVisible = billboard.visible; + // + // // Set a single property + // billboard.position = { x: 1, y: 3, z: 2 }; + // + // // Set multiple properties at the same time + // billboard.setProperties({ + // url: "http://images.com/overlayImage.jpg", + // dimensions: { x: 2, y: 2 } + // }); + // + // // Clone an overlay + // var clonedBillboard = billboard.clone(); + // + // // Remove an overlay from the world + // billboard.destroy(); + // + // // Remember, there is a poor orphaned JavaScript object left behind. You should + // // remove any references to it so you don't accidentally try to modify an overlay that + // // isn't there. + // billboard = undefined; + // + (function() { + var ABSTRACT = null; + overlayTypes = {}; + + function generateOverlayClass(superclass, type, properties) { + var that; + if (type == ABSTRACT) { + that = function(type, params) { + superclass.call(this, type, params); + }; + } else { + that = function(params) { + superclass.call(this, type, params); + }; + overlayTypes[type] = that; + } + + that.prototype = new superclass(); + that.prototype.constructor = that; + + properties.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + return that; + } + + // Supports multiple inheritance of properties. Just `concat` them onto the end of the + // properties list. + var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"]; + + Overlay = (function() { + var that = function(type, params) { + if (type && params) { + this._id = Overlays.addOverlay(type, params); + overlays[this._id] = this; + } else { + this._id = 0; + } + this._attachedPanelPointer = null; + }; + + that.prototype.constructor = that; + + Object.defineProperty(that.prototype, "isLoaded", { + get: function() { + return Overlays.isLoaded(this._id); + } + }); + + Object.defineProperty(that.prototype, "attachedPanel", { + get: function() { + return this._attachedPanelPointer; + } + }); + + that.prototype.getTextSize = function(text) { + return Overlays.textSize(this._id, text); + }; + + that.prototype.setProperties = function(properties) { + Overlays.editOverlay(this._id, properties); + }; + + that.prototype.clone = function() { + return makeOverlayFromId(Overlays.cloneOverlay(this._id)); + }; + + that.prototype.destroy = function() { + Overlays.deleteOverlay(this._id); + }; + + return generateOverlayClass(that, ABSTRACT, [ + "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", + "alphaPulse", "colorPulse", "visible", "anchor" + ]); + })(); + + Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [ + "bounds", "x", "y", "width", "height" + ]); + + Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ + "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", + "ignoreRayIntersection", "drawInFront", "drawOnHUD" + ]); + + Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ + "dimensions" + ]); + + generateOverlayClass(Overlay2D, "image", [ + "subImage", "imageURL" + ]); + + generateOverlayClass(Overlay2D, "text", [ + "font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin" + ]); + + generateOverlayClass(Planar3DOverlay, "text3d", [ + "text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin", + "rightMargin", "bottomMargin", "isFacingAvatar" + ]); + + generateOverlayClass(Volume3DOverlay, "cube", [ + "borderSize" + ]); + + generateOverlayClass(Volume3DOverlay, "sphere", [ + ]); + + generateOverlayClass(Planar3DOverlay, "circle3d", [ + "startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks", + "majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength", + "minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor" + ]); + + generateOverlayClass(Planar3DOverlay, "rectangle3d", [ + ]); + + generateOverlayClass(Base3DOverlay, "line3d", [ + "start", "end" + ]); + + generateOverlayClass(Planar3DOverlay, "grid", [ + "minorGridWidth", "majorGridEvery" + ]); + + generateOverlayClass(Volume3DOverlay, "localmodels", [ + ]); + + generateOverlayClass(Volume3DOverlay, "model", [ + "url", "dimensions", "textures" + ]); + + generateOverlayClass(Planar3DOverlay, "billboard", [ + "url", "subImage", "isFacingAvatar" + ].concat(PANEL_ATTACHABLE_FIELDS)); + })(); + + ImageOverlay = overlayTypes["image"]; + TextOverlay = overlayTypes["text"]; + Text3DOverlay = overlayTypes["text3d"]; + Cube3DOverlay = overlayTypes["cube"]; + Sphere3DOverlay = overlayTypes["sphere"]; + Circle3DOverlay = overlayTypes["circle3d"]; + Rectangle3DOverlay = overlayTypes["rectangle3d"]; + Line3DOverlay = overlayTypes["line3d"]; + Grid3DOverlay = overlayTypes["grid"]; + LocalModelsOverlay = overlayTypes["localmodels"]; + ModelOverlay = overlayTypes["model"]; + BillboardOverlay = overlayTypes["billboard"]; + + + // + // Object oriented abstraction layer for panels. + // + FloatingUIPanel = (function() { + var that = function(params) { + this._id = Overlays.addPanel(params); + this._children = []; + this._visible = Boolean(params.visible); + panels[this._id] = this; + this._attachedPanelPointer = null; + }; + + that.prototype.constructor = that; + + var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; + FIELDS.forEach(function(prop) { + Object.defineProperty(that.prototype, prop, { + get: function() { + return Overlays.getPanelProperty(this._id, prop); + }, + set: function(newValue) { + var keyValuePair = {}; + keyValuePair[prop] = newValue; + this.setProperties(keyValuePair); + }, + configurable: false + }); + }); + + var PSEUDO_FIELDS = []; + + PSEUDO_FIELDS.push("children"); + Object.defineProperty(that.prototype, "children", { + get: function() { + return this._children.slice(); + } + }); + + PSEUDO_FIELDS.push("visible"); + Object.defineProperty(that.prototype, "visible", { + get: function() { + return this._visible; + }, + set: function(visible) { + this._visible = visible; + this._children.forEach(function(child) { + child.visible = visible; + }); + } + }); + + that.prototype.addChild = function(child) { + if (child instanceof Overlay) { + Overlays.setAttachedPanel(child._id, this._id); + } else if (child instanceof FloatingUIPanel) { + child.setProperties({ + anchorPosition: { + bind: "panel", + value: this._id + }, + offsetRotation: { + bind: "panel", + value: this._id + } + }); + } + child._attachedPanelPointer = this; + child.visible = this.visible; + this._children.push(child); + return child; + }; + + that.prototype.removeChild = function(child) { + var i = this._children.indexOf(child); + if (i >= 0) { + if (child instanceof Overlay) { + Overlays.setAttachedPanel(child._id, 0); + } else if (child instanceof FloatingUIPanel) { + child.setProperties({ + anchorPosition: { + bind: "myAvatar" + }, + offsetRotation: { + bind: "myAvatar" + } + }); + } + child._attachedPanelPointer = null; + this._children.splice(i, 1); + } + }; + + that.prototype.setProperties = function(properties) { + for (var i in PSEUDO_FIELDS) { + if (properties[PSEUDO_FIELDS[i]] !== undefined) { + this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]]; + } + } + Overlays.editPanel(this._id, properties); + }; + + that.prototype.destroy = function() { + Overlays.deletePanel(this._id); + }; + + return that; + })(); + + + function onOverlayDeleted(id) { + if (id in overlays) { + if (overlays[id]._attachedPanelPointer) { + overlays[id]._attachedPanelPointer.removeChild(overlays[id]); + } + delete overlays[id]; + } + } + + function onPanelDeleted(id) { + if (id in panels) { + panels[id]._children.forEach(function(child) { + print(JSON.stringify(child.destroy)); + child.destroy(); + }); + delete panels[id]; + } + } + + Overlays.overlayDeleted.connect(onOverlayDeleted); + Overlays.panelDeleted.connect(onPanelDeleted); +})(); diff --git a/examples/libraries/overlayUtils.js b/examples/libraries/overlayUtils.js index 3acab0103b..a5622ec435 100644 --- a/examples/libraries/overlayUtils.js +++ b/examples/libraries/overlayUtils.js @@ -2,39 +2,16 @@ // overlayUtils.js // examples/libraries // -// Modified by Zander Otavka on 7/15/15 -// Copyright 2014 High Fidelity, Inc. -// -// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods. -// Instead of: -// -// var billboard = Overlays.addOverlay("billboard", { visible: false }); -// ... -// Overlays.editOverlay(billboard, { visible: true }); -// ... -// Overlays.deleteOverlay(billboard); -// -// You can now do: -// -// var billboard = new BillboardOverlay({ visible: false }); -// ... -// billboard.visible = true; -// ... -// billboard.destroy(); -// -// See more on usage below. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// Copyright 2015 High Fidelity, Inc. // -/** - * DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. - * - * OverlayGroup provides a way to create composite overlays and control their - * position relative to a settable rootPosition and rootRotation. - */ +// +// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel. +// +// OverlayGroup provides a way to create composite overlays and control their +// position relative to a settable rootPosition and rootRotation. +// OverlayGroup = function(opts) { var that = {}; @@ -92,226 +69,6 @@ OverlayGroup = function(opts) { } overlays = {}; } - + return that; }; - - -/** - * Object oriented abstraction layer for overlays. - * - * Usage: - * // Create an overlay - * var billboard = new BillboardOverlay({ - * visible: true, - * isFacingAvatar: true, - * ignoreRayIntersections: false - * }); - * - * // Get a property - * var isVisible = billboard.visible; - * - * // Set a single property - * billboard.position = { x: 1, y: 3, z: 2 }; - * - * // Set multiple properties at the same time - * billboard.setProperties({ - * url: "http://images.com/overlayImage.jpg", - * dimensions: { x: 2, y: 2 } - * }); - * - * // Clone an overlay - * var clonedBillboard = billboard.clone(); - * - * // Remove an overlay from the world - * billboard.destroy(); - * - * // Remember, there is a poor orphaned JavaScript object left behind. You should remove any - * // references to it so you don't accidentally try to modify an overlay that isn't there. - * billboard = undefined; - */ -(function() { - var ABSTRACT = null; - - function generateOverlayClass(superclass, type, properties) { - var that; - if (type == ABSTRACT) { - that = function(type, params) { - superclass.apply(this, [type, params]); - }; - } else { - that = function(params) { - superclass.apply(this, [type, params]); - }; - } - - that.prototype = new superclass(); - that.prototype.constructor = that; - - properties.forEach(function(prop) { - Object.defineProperty(that.prototype, prop, { - get: function() { - return Overlays.getProperty(this._id, prop); - }, - set: function(newValue) { - var keyValuePair = {}; - keyValuePair[prop] = newValue; - this.setProperties(keyValuePair); - }, - configurable: true - }); - }); - - return that; - } - - - // Supports multiple inheritance of properties. Just `concat` them onto the end of the - // properties list. - var PANEL_ATTACHABLE_FIELDS = ["attachedPanel"]; - - // TODO: finish exposing all overlay classes. - - var Overlay = (function() { - var BaseOverlay = (function() { - var that = function(type, params) { - Object.apply(this, []); - if (type && params) { - this._type = type; - this._id = Overlays.addOverlay(type, params); - } else { - this._type = ""; - this._id = 0; - } - this._attachedPanelPointer = null; - }; - - that.prototype = new Object(); - that.prototype.constructor = that; - - Object.defineProperty(that.prototype, "overlayType", { - get: function() { - return this._type; - } - }); - - that.prototype.setProperties = function(properties) { - Overlays.editOverlay(this._id, properties); - }; - - that.prototype.clone = function() { - var clone = new this.constructor(); - clone._type = this._type; - clone._id = Overlays.cloneOverlay(this._id); - if (this._attachedPanelPointer) { - this._attachedPanelPointer.addChild(clone); - } - return clone; - }; - - that.prototype.destroy = function() { - Overlays.deleteOverlay(this._id); - }; - - return that; - }()); - - return generateOverlayClass(BaseOverlay, ABSTRACT, [ - "alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse", - "alphaPulse", "colorPulse", "visible", "anchor" - ]); - }()); - - var Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [ - "position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine", - "ignoreRayIntersection", "drawInFront", "drawOnHUD" - ]); - - var Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [ - "dimensions" - ]); - - BillboardOverlay = generateOverlayClass(Planar3DOverlay, "billboard", [ - "url", "subImage", "isFacingAvatar", "offsetPosition" - ].concat(PANEL_ATTACHABLE_FIELDS)); -}()); - - -/** - * Object oriented abstraction layer for panels. - */ -FloatingUIPanel = (function() { - var that = function(params) { - this._id = Overlays.addPanel(params); - this._children = []; - }; - - var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"]; - FIELDS.forEach(function(prop) { - Object.defineProperty(that.prototype, prop, { - get: function() { - return Overlays.getPanelProperty(this._id, prop); - }, - set: function(newValue) { - var keyValuePair = {}; - keyValuePair[prop] = newValue; - this.setProperties(keyValuePair); - }, - configurable: false - }); - }); - - Object.defineProperty(that.prototype, "children", { - get: function() { - return this._children.slice(); - } - }) - - that.prototype.addChild = function(overlay) { - overlay.attachedPanel = this._id; - overlay._attachedPanelPointer = this; - this._children.push(overlay); - return overlay; - }; - - that.prototype.removeChild = function(overlay) { - var i = this._children.indexOf(overlay); - if (i >= 0) { - overlay.attachedPanel = 0; - overlay._attachedPanelPointer = null; - this._children.splice(i, 1); - } - }; - - that.prototype.setVisible = function(visible) { - for (var i in this._children) { - this._children[i].visible = visible; - } - }; - - that.prototype.setProperties = function(properties) { - Overlays.editPanel(this._id, properties); - }; - - that.prototype.destroy = function() { - Overlays.deletePanel(this._id); - var i = _panels.indexOf(this); - if (i >= 0) { - _panels.splice(i, 1); - } - }; - - that.prototype.findRayIntersection = function(pickRay) { - var rayPickResult = Overlays.findRayIntersection(pickRay); - if (rayPickResult.intersects) { - for (var i in this._children) { - if (this._children[i]._id == rayPickResult.overlayID) { - return this._children[i]; - } - } - } - return null; - }; - - return that; -}()); From 37a1ad76262a1b9b7e109548e012ee7640e0777b Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:02:18 -0700 Subject: [PATCH 089/109] Improve floatingUIExample.js. Demonstrates overlayManager.js functionality. --- examples/example/ui/floatingUIExample.js | 247 ++++++++++++----------- 1 file changed, 133 insertions(+), 114 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 3b555efce3..9c4e43be94 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -10,140 +10,159 @@ // Script.include([ - "../../libraries/globals.js", - "../../libraries/overlayUtils.js", + "../../libraries/globals.js", + "../../libraries/overlayManager.js", ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg"; var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg"; -var BLANK_ROTATION = { x: 0, y: 0, z: 0, w: 0 }; - -function isBlank(rotation) { - return rotation.x == BLANK_ROTATION.x && - rotation.y == BLANK_ROTATION.y && - rotation.z == BLANK_ROTATION.z && - rotation.w == BLANK_ROTATION.w; -} - -var panel = new FloatingUIPanel({ - offsetPosition: { x: 0, y: 0, z: 1 }, +var mainPanel = new FloatingUIPanel({ + offsetRotation: { + bind: "quat", + value: { w: 1, x: 0, y: 0, z: 0 } + }, + offsetPosition: { x: 0, y: 0.4, z: 1 } }); -var bg = panel.addChild(new BillboardOverlay({ - url: BG_IMAGE_URL, - dimensions: { - x: 0.5, - y: 0.5, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, +var bluePanel = mainPanel.addChild(new FloatingUIPanel ({ + offsetPosition: { x: 0.1, y: 0.1, z: -0.2 } })); -var redDot = panel.addChild(new BillboardOverlay({ - url: RED_DOT_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: -0.15, - y: -0.15, - z: -0.001 - } +var mainPanelBackground = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0, + y: 0, + z: 0.001 + } +}); + +var bluePanelBackground = mainPanelBackground.clone(); +bluePanelBackground.dimensions = { + x: 0.3, + y: 0.3 +}; + +mainPanel.addChild(mainPanelBackground); +bluePanel.addChild(bluePanelBackground); + +var redDot = mainPanel.addChild(new BillboardOverlay({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.15, + y: -0.15, + z: 0 + } })); -var redDot2 = panel.addChild(new BillboardOverlay({ - url: RED_DOT_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: -0.15, - y: 0, - z: -0.001 - } +var redDot2 = mainPanel.addChild(new BillboardOverlay({ + url: RED_DOT_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.155, + y: 0.005, + z: 0 + } })); -var blueSquare = panel.addChild(new BillboardOverlay({ - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: 0.1, - y: 0, - z: -0.001 - } +var blueSquare = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: -0.055, + z: 0 + } })); -var blueSquare2 = panel.addChild(new BillboardOverlay({ - url: BLUE_SQUARE_IMAGE_URL, - dimensions: { - x: 0.1, - y: 0.1, - }, - isFacingAvatar: false, - visible: true, - alpha: 1.0, - ignoreRayIntersection: false, - offsetPosition: { - x: 0.1, - y: 0.11, - z: -0.001 - } +var blueSquare2 = bluePanel.addChild(new BillboardOverlay({ + url: BLUE_SQUARE_IMAGE_URL, + dimensions: { + x: 0.1, + y: 0.1, + }, + isFacingAvatar: false, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.055, + y: 0.055, + z: 0 + } })); var blueSquare3 = blueSquare2.clone(); blueSquare3.offsetPosition = { - x: -0.01, - y: 0.11, - z: -0.001 + x: -0.055, + y: 0.055, + z: 0 }; -blueSquare3.ignoreRayIntersection = false; -Controller.mousePressEvent.connect(function(event) { - if (event.isRightButton) { - var newOffsetRotation; - print(JSON.stringify(panel.offsetRotation)) - if (isBlank(panel.offsetRotation)) { - newOffsetRotation = Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }); - } else { - newOffsetRotation = BLANK_ROTATION; - } - panel.offsetRotation = newOffsetRotation; - } else if (event.isLeftButton) { - var pickRay = Camera.computePickRay(event.x, event.y) - var overlay = panel.findRayIntersection(pickRay); - if (overlay) { - var oldPos = overlay.offsetPosition; - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - } - overlay.offsetPosition = newPos; - } - } -}); -Script.scriptEnding.connect(function() { - panel.destroy(); -}); \ No newline at end of file +function onMouseDown(event) { + isLeftClick = event.isLeftButton; + isRightClick = event.isRightButton; +} + +function onMouseMove(event) { + isLeftClick = isRightClick = false; +} + +function onMouseUp(event) { + if (isLeftClick && event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + print(overlay.attachedPanel); + if (overlay.attachedPanel === bluePanel) { + overlay.destroy(); + } else if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + }; + overlay.offsetPosition = newPos; + } + } + if (isRightClick && event.isRightButton) { + mainPanel.visible = !mainPanel.visible; + } + isLeftClick = isRightClick = false; +} + +function onScriptEnd() { + mainPanel.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); +Controller.mouseReleaseEvent.connect(onMouseUp); +Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file From 3983c31219f4de1b93fb861d83e2fe9bebb13fcf Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 18:03:21 -0700 Subject: [PATCH 090/109] Add controlPanel.js script. Right click to open a control panel. Control panel allows toggling of face mute, mic mute, and address bar. --- examples/controlPanel.js | 221 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 examples/controlPanel.js diff --git a/examples/controlPanel.js b/examples/controlPanel.js new file mode 100644 index 0000000000..6373dfc921 --- /dev/null +++ b/examples/controlPanel.js @@ -0,0 +1,221 @@ +// +// controlPanel.js +// examples +// +// Created by Zander Otavka on 7/15/15. +// Copyright 2015 High Fidelity, Inc. +// +// Shows a few common controls in a FloatingUIPanel on right click. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include([ + "libraries/globals.js", + "libraries/overlayManager.js", +]); + +var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; +var CLOSE_IMAGE_URL = "file:///Users/zander/Desktop/assets/close.svg"; +var MIC_IMAGE_URL = "file:///Users/zander/Desktop/assets/mic-composite.svg"; +var FACE_IMAGE_URL = "file:///Users/zander/Desktop/assets/face-composite.svg"; +var ADDRESS_BAR_IMAGE_URL = "file:///Users/zander/Desktop/assets/address-bar-composite.svg"; + +var panel = new FloatingUIPanel({ + anchorPosition: { + bind: "myAvatar" + }, + offsetPosition: { x: 0, y: 0.4, z: 1 } +}); + +var background = new BillboardOverlay({ + url: BG_IMAGE_URL, + dimensions: { + x: 0.5, + y: 0.5, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false +}); +panel.addChild(background); + +var closeButton = new BillboardOverlay({ + url: CLOSE_IMAGE_URL, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.1, + y: 0.1, + z: -0.001 + } +}); +closeButton.onClick = function(event) { + panel.visible = false; +}; +panel.addChild(closeButton); + +var micMuteButton = new BillboardOverlay({ + url: MIC_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: 0.1, + z: -0.001 + } +}); +micMuteButton.onClick = function(event) { + AudioDevice.toggleMute(); +}; +panel.addChild(micMuteButton); + +var faceMuteButton = new BillboardOverlay({ + url: FACE_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: 0.1, + y: -0.1, + z: -0.001 + } +}); +faceMuteButton.onClick = function(event) { + FaceTracker.toggleMute(); +}; +panel.addChild(faceMuteButton); + +var addressBarButton = new BillboardOverlay({ + url: ADDRESS_BAR_IMAGE_URL, + subImage: { + x: 0, + y: 0, + width: 45, + height: 45 + }, + dimensions: { + x: 0.15, + y: 0.15, + }, + isFacingAvatar: false, + visible: true, + alpha: 1.0, + ignoreRayIntersection: false, + offsetPosition: { + x: -0.1, + y: -0.1, + z: -0.001 + } +}); +addressBarButton.onClick = function(event) { + DialogsManager.toggleAddressBar(); +}; +panel.addChild(addressBarButton); + + +function onMicMuteToggled() { + var offset; + if (AudioDevice.getMuted()) { + offset = 45; + } else { + offset = 0; + } + micMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onMicMuteToggled(); + +function onFaceMuteToggled() { + var offset; + if (FaceTracker.getMuted()) { + offset = 45; + } else { + offset = 0; + } + faceMuteButton.subImage = { + x: offset, + y: 0, + width: 45, + height: 45 + }; +} +onFaceMuteToggled(); + +var isLeftClick = false, + isRightClick = false; + +function onMouseDown(event) { + isLeftClick = event.isLeftButton; + isRightClick = event.isRightButton; +} + +function onMouseMove(event) { + isLeftClick = isRightClick = false; +} + +function onMouseUp(event) { + if (isLeftClick && event.isLeftButton) { + var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + if (overlay && overlay.onClick) { + overlay.onClick(event); + } + } + if (isRightClick && event.isRightButton) { + panel.setProperties({ + visible: true, + offsetRotation: { + bind: "quat", + value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) + } + }); + } + isLeftClick = isRightClick = false; +} + +function onScriptEnd(event) { + panel.destroy(); + panel2.destroy(); +} + +Controller.mousePressEvent.connect(onMouseDown); +Controller.mouseMoveEvent.connect(onMouseMove); +Controller.mouseReleaseEvent.connect(onMouseUp); +AudioDevice.muteToggled.connect(onMicMuteToggled); +FaceTracker.muteToggled.connect(onFaceMuteToggled); +Script.scriptEnding.connect(onScriptEnd); From c37c3ec2d2f7ed5f31abf290230a7d8ac72d2746 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 19:30:30 -0700 Subject: [PATCH 091/109] Use images off the amazon bucket, not file://. --- examples/controlPanel.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index 6373dfc921..837b59ffa0 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -17,10 +17,10 @@ Script.include([ ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; -var CLOSE_IMAGE_URL = "file:///Users/zander/Desktop/assets/close.svg"; -var MIC_IMAGE_URL = "file:///Users/zander/Desktop/assets/mic-composite.svg"; -var FACE_IMAGE_URL = "file:///Users/zander/Desktop/assets/face-composite.svg"; -var ADDRESS_BAR_IMAGE_URL = "file:///Users/zander/Desktop/assets/address-bar-composite.svg"; +var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/close.svg"; +var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/mic-toggle.svg"; +var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/face-toggle.svg"; +var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/address-bar-toggle.svg"; var panel = new FloatingUIPanel({ anchorPosition: { @@ -36,7 +36,6 @@ var background = new BillboardOverlay({ y: 0.5, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false }); @@ -49,7 +48,6 @@ var closeButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -76,7 +74,6 @@ var micMuteButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -103,7 +100,6 @@ var faceMuteButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -130,7 +126,6 @@ var addressBarButton = new BillboardOverlay({ y: 0.15, }, isFacingAvatar: false, - visible: true, alpha: 1.0, ignoreRayIntersection: false, offsetPosition: { @@ -198,7 +193,7 @@ function onMouseUp(event) { } if (isRightClick && event.isRightButton) { panel.setProperties({ - visible: true, + visible: !panel.visible, offsetRotation: { bind: "quat", value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 }) @@ -210,7 +205,6 @@ function onMouseUp(event) { function onScriptEnd(event) { panel.destroy(); - panel2.destroy(); } Controller.mousePressEvent.connect(onMouseDown); From 9bd72e5769c7348b6791a6b5c7273a2c4af0c1dc Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 24 Jul 2015 20:07:38 -0700 Subject: [PATCH 092/109] Tweak PanelAttachable::setTransforms. Use reference instead of pointer, and make it virtual. --- interface/src/ui/overlays/BillboardOverlay.cpp | 8 ++++---- interface/src/ui/overlays/BillboardOverlay.h | 2 +- interface/src/ui/overlays/PanelAttachable.cpp | 11 +++++------ interface/src/ui/overlays/PanelAttachable.h | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 8ffa909e12..3bd1f56ba3 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -39,7 +39,7 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) : { } -void BillboardOverlay::setTransforms(Transform *transform) { +void BillboardOverlay::setTransforms(Transform& transform) { PanelAttachable::setTransforms(transform); if (_isFacingAvatar) { glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); @@ -49,7 +49,7 @@ void BillboardOverlay::setTransforms(Transform *transform) { } void BillboardOverlay::update(float deltatime) { - setTransforms(&_transform); + setTransforms(_transform); } void BillboardOverlay::render(RenderArgs* args) { @@ -98,7 +98,7 @@ void BillboardOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - setTransforms(&_transform); + setTransforms(_transform); Transform transform = _transform; transform.postScale(glm::vec3(getDimensions(), 1.0f)); @@ -197,7 +197,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v float& distance, BoxFace& face) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - setTransforms(&_transform); + setTransforms(_transform); // Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 47be764f2f..a034612e71 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -43,7 +43,7 @@ public: virtual BillboardOverlay* createClone() const; protected: - void setTransforms(Transform* transform); + virtual void setTransforms(Transform& transform); private: void setBillboardURL(const QString& url); diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index e114fa143e..ac4730cfe8 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -26,13 +26,12 @@ PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) : { } -void PanelAttachable::setTransforms(Transform* transform) { - Q_ASSERT(transform != nullptr); +void PanelAttachable::setTransforms(Transform& transform) { if (getAttachedPanel()) { - transform->setTranslation(getAttachedPanel()->getAnchorPosition()); - transform->setRotation(getAttachedPanel()->getOffsetRotation()); - transform->postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); - transform->postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); + transform.setTranslation(getAttachedPanel()->getAnchorPosition()); + transform.setRotation(getAttachedPanel()->getOffsetRotation()); + transform.postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition()); + transform.postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation()); } } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index e1fc490d00..9776ac5ecb 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -34,7 +34,7 @@ public: void setProperties(const QScriptValue& properties); protected: - void setTransforms(Transform* transform); + virtual void setTransforms(Transform& transform); private: FloatingUIPanel::Pointer _attachedPanel; From 7973c2d469f1be8747488cfbe690476887e922a6 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 31 Jul 2015 10:55:51 -0700 Subject: [PATCH 093/109] Fix compile issue on Linux and Windows. Shoutout to Andrew for help finding the missing #include. --- interface/src/ui/overlays/FloatingUIPanel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/overlays/FloatingUIPanel.h b/interface/src/ui/overlays/FloatingUIPanel.h index f65fa93c26..f84ac32fac 100644 --- a/interface/src/ui/overlays/FloatingUIPanel.h +++ b/interface/src/ui/overlays/FloatingUIPanel.h @@ -13,6 +13,7 @@ #define hifi_FloatingUIPanel_h #include +#include #include #include From b0afdb21ad3daf1b6ae6b139d39f32ba4c9fe48f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jul 2015 14:29:05 -0700 Subject: [PATCH 094/109] correct the unique_ptr char allocations --- assignment-client/src/Agent.cpp | 2 +- ice-server/src/IceServer.cpp | 2 +- libraries/networking/src/NLPacket.cpp | 4 ++-- libraries/networking/src/NLPacket.h | 4 ++-- libraries/networking/src/PacketReceiver.cpp | 2 +- libraries/networking/src/udt/Packet.cpp | 6 +++--- libraries/networking/src/udt/Packet.h | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index da588bc316..43866ce3a6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -70,7 +70,7 @@ void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointe // pull out the piggybacked packet and create a new QSharedPointer for it int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength; - std::unique_ptr buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr()); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 46c1582e7d..2a4e2b4300 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -50,7 +50,7 @@ void IceServer::processDatagrams() { while (_serverSocket.hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = _serverSocket.pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); _serverSocket.readDatagram(buffer.get(), packetSizeWithHeader, sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index 7a6503dbc3..7ec10fc4ce 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -46,7 +46,7 @@ std::unique_ptr NLPacket::create(PacketType::Value type, qint64 size) return packet; } -std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, +std::unique_ptr NLPacket::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with null data Q_ASSERT(data); @@ -85,7 +85,7 @@ NLPacket::NLPacket(const NLPacket& other) : Packet(other) { } -NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +NLPacket::NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : Packet(std::move(data), size, senderSockAddr) { adjustPayloadStartAndCapacity(); diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h index 669278ed65..6ebf15ffec 100644 --- a/libraries/networking/src/NLPacket.h +++ b/libraries/networking/src/NLPacket.h @@ -20,7 +20,7 @@ class NLPacket : public Packet { Q_OBJECT public: static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const NLPacket& other); @@ -45,7 +45,7 @@ protected: NLPacket(PacketType::Value type); NLPacket(PacketType::Value type, qint64 size); - NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + NLPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); NLPacket(const NLPacket& other); void readSourceID(); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 5fc327673d..b1a77d4c39 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -243,7 +243,7 @@ void PacketReceiver::processDatagrams() { while (nodeList && nodeList->getNodeSocket().hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = nodeList->getNodeSocket().pendingDatagramSize(); - std::unique_ptr buffer = std::unique_ptr(new char[packetSizeWithHeader]); + auto buffer = std::unique_ptr(new char[packetSizeWithHeader]); // if we're supposed to drop this packet then break out here if (_shouldDropPackets) { diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index 02a44c4a4f..13f8a39e26 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -31,7 +31,7 @@ std::unique_ptr Packet::create(PacketType::Value type, qint64 size) { return packet; } -std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { +std::unique_ptr Packet::fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) { // Fail with invalid size Q_ASSERT(size >= 0); @@ -82,7 +82,7 @@ Packet::Packet(PacketType::Value type, qint64 size) : } } -Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : +Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr) : _packetSize(size), _packet(std::move(data)), _senderSockAddr(senderSockAddr) @@ -110,7 +110,7 @@ Packet& Packet::operator=(const Packet& other) { _type = other._type; _packetSize = other._packetSize; - _packet = std::unique_ptr(new char[_packetSize]); + _packet = std::unique_ptr(new char[_packetSize]); memcpy(_packet.get(), other._packet.get(), _packetSize); _payloadStart = _packet.get() + (other._payloadStart - other._packet.get()); diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index b4c53b8165..91b5974e09 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -27,7 +27,7 @@ public: static const qint64 PACKET_WRITE_ERROR; static std::unique_ptr create(PacketType::Value type, qint64 size = -1); - static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); // Provided for convenience, try to limit use static std::unique_ptr createCopy(const Packet& other); @@ -88,7 +88,7 @@ public: protected: Packet(PacketType::Value type, qint64 size); - Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); + Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); Packet(const Packet& other); Packet& operator=(const Packet& other); Packet(Packet&& other); @@ -109,7 +109,7 @@ protected: PacketVersion _version; // Packet version qint64 _packetSize = 0; // Total size of the allocated memory - std::unique_ptr _packet; // Allocated memory + std::unique_ptr _packet; // Allocated memory char* _payloadStart = nullptr; // Start of the payload qint64 _payloadCapacity = 0; // Total capacity of the payload From c88e0360b3714890b9f6ba1774514f246b27a672 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 31 Jul 2015 14:46:43 -0700 Subject: [PATCH 095/109] Change the inspect.js pan direction and rate This makes camera zoom, orbit, and panning more similar to Second Life. --- examples/inspect.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/inspect.js b/examples/inspect.js index 0df90fbac3..555b4105b7 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -23,7 +23,7 @@ var RAD_TO_DEG = 180.0 / PI; var AZIMUTH_RATE = 90.0; var ALTITUDE_RATE = 200.0; var RADIUS_RATE = 1.0 / 100.0; -var PAN_RATE = 50.0; +var PAN_RATE = 250.0; var Y_AXIS = { x: 0, @@ -139,7 +139,7 @@ function handlePanMode(dx, dy) { var right = Quat.getRight(Camera.getOrientation()); var distance = Vec3.length(vector); - var dv = Vec3.sum(Vec3.multiply(up, -distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE)); + var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE)); center = Vec3.sum(center, dv); position = Vec3.sum(position, dv); From 12ad60a6b5afa77c73b4d899922afa5e637e04f8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 31 Jul 2015 14:48:54 -0700 Subject: [PATCH 096/109] add a missed char[] in OctreePacketProcessor --- interface/src/octree/OctreePacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 7bb94323b7..1abbb21089 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -57,7 +57,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share if (piggybackBytes) { // construct a new packet from the piggybacked one - std::unique_ptr buffer = std::unique_ptr(new char[piggybackBytes]); + auto buffer = std::unique_ptr(new char[piggybackBytes]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes); auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr()); From 0dad1adf7d928fc6cc8ef5ec616b92b3a3cf0b2e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 31 Jul 2015 18:05:14 -0700 Subject: [PATCH 097/109] Speculative guard against crashing. --- libraries/render-utils/src/Model.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1dc08b7a06..72a601cd20 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -243,6 +243,9 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { + if (!_geometry) { + return; + } const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; _rig->initJointTransforms(parentTransform); @@ -424,6 +427,9 @@ bool Model::updateGeometry() { deleteGeometry(); _dilatedTextures.clear(); + if (!geometry) { + std::cout << "WARNING: no geometry in Model::updateGeometry\n"; + } setGeometry(geometry); _meshGroupsKnown = false; From 1dd6c1117d434ef1555ca7f5b698b6b557fea153 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 11:29:28 -0700 Subject: [PATCH 098/109] change GeometryCache::renderQuad() to use TRIANGLES as lower level render primitive --- libraries/gpu/src/gpu/Format.h | 2 + libraries/model/src/model/Geometry.h | 2 +- libraries/render-utils/src/GeometryCache.cpp | 97 ++++++++++++++++---- libraries/render-utils/src/GeometryCache.h | 2 + 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 981a560965..01d3f37ef8 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -213,6 +213,8 @@ enum Primitive { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, + + // FIXME - remove these QUADS, QUAD_STRIP, diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 16ebb60b72..5ef414a2d1 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -71,7 +71,7 @@ public: LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, - QUADS, + QUADS, // NOTE: These must be translated to triangles before rendering QUAD_STRIP, NUM_TOPOLOGIES, diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index def5f38db4..12a6d46be6 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1012,21 +1012,27 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2; // vertices - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1037,7 +1043,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, maxCorner.x, maxCorner.y, @@ -1050,14 +1056,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderUnitCube(gpu::Batch& batch) { @@ -1102,23 +1118,29 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2 * 2; // text coords & vertices - const int vertices = 4; + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 2; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1130,7 +1152,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, @@ -1144,14 +1166,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { @@ -1177,21 +1209,27 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co } const int FLOATS_PER_VERTEX = 3; // vertices - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1202,7 +1240,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, maxCorner.x, maxCorner.y, maxCorner.z, @@ -1215,14 +1253,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1267,23 +1315,29 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } const int FLOATS_PER_VERTEX = 3 + 2; // 3d vertices + text coords - const int vertices = 4; + const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles + const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 3; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); if (!details.isCreated) { details.isCreated = true; - details.vertices = vertices; + details.vertices = VERTICES; + details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); + auto indicesBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; + details.indicesBuffer = indicesBuffer; + details.streamFormat = streamFormat; details.stream = stream; @@ -1295,7 +1349,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, @@ -1309,13 +1363,24 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; + details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.draw(gpu::QUADS, 4, 0); + batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 812d12b846..19e84e9346 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -235,10 +235,12 @@ private: static int population; gpu::BufferPointer verticesBuffer; gpu::BufferPointer colorBuffer; + gpu::BufferPointer indicesBuffer; gpu::Stream::FormatPointer streamFormat; gpu::BufferStreamPointer stream; int vertices; + int indices; int vertexSize; bool isCreated; From 452225ddaf87ba387f6c06ffc96e863f2dfc95be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 11:51:07 -0700 Subject: [PATCH 099/109] removed QUADS from Font::drawString() --- libraries/render-utils/src/text/Font.cpp | 30 ++++++++++++++++++++++-- libraries/render-utils/src/text/Font.h | 2 ++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index b341f444e6..3cedcdcbc1 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -18,8 +18,12 @@ struct TextureVertex { TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {} }; +static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles +static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices + struct QuadBuilder { - TextureVertex vertices[4]; + TextureVertex vertices[VERTICES_PER_QUAD]; + QuadBuilder(const glm::vec2& min, const glm::vec2& size, const glm::vec2& texMin, const glm::vec2& texSize) { // min = bottomLeft @@ -249,6 +253,9 @@ void Font::setupGPU() { void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2& bounds) { _verticesBuffer = std::make_shared(); _numVertices = 0; + _indicesBuffer = std::make_shared(); + _numIndices = 0; + _lastStringRendered = str; _lastBounds = bounds; @@ -284,10 +291,28 @@ void Font::rebuildVertices(float x, float y, const QString& str, const glm::vec2 if (!isNewLine) { for (auto c : token) { auto glyph = _glyphs[c]; + quint16 verticesOffset = _numVertices; QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + quint16 indices[NUMBER_OF_INDICES_PER_QUAD]; + indices[0] = verticesOffset + 0; + indices[1] = verticesOffset + 1; + indices[2] = verticesOffset + 3; + indices[3] = verticesOffset + 1; + indices[4] = verticesOffset + 2; + indices[5] = verticesOffset + 3; + _indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices); + _numIndices += NUMBER_OF_INDICES_PER_QUAD; + // Advance by glyph size advance.x += glyph.d; @@ -318,5 +343,6 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); - batch.draw(gpu::QUADS, _numVertices, 0); + batch.setIndexBuffer(gpu::UINT16, _indicesBuffer, 0); + batch.drawIndexed(gpu::TRIANGLES, _numIndices, 0); } diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h index 55801419f9..e10360d45f 100644 --- a/libraries/render-utils/src/text/Font.h +++ b/libraries/render-utils/src/text/Font.h @@ -64,8 +64,10 @@ private: gpu::TexturePointer _texture; gpu::Stream::FormatPointer _format; gpu::BufferPointer _verticesBuffer; + gpu::BufferPointer _indicesBuffer; gpu::BufferStreamPointer _stream; unsigned int _numVertices = 0; + unsigned int _numIndices = 0; int _fontLoc = -1; int _outlineLoc = -1; From 05a4a6aa9bb99c6b55d38d6127d3f18db711d08b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 13:07:56 -0700 Subject: [PATCH 100/109] implement on-the-fly conversion of FBXMeshParts that are quads, into their triangle equivalents --- libraries/fbx/src/FBXReader.cpp | 51 +++++++++++++++++++++++++++- libraries/fbx/src/FBXReader.h | 11 ++++-- libraries/render-utils/src/Model.cpp | 5 ++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e18b334264..51f91de71a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -830,6 +830,56 @@ public: std::vector attributes; }; +gpu::BufferPointer FBXMeshPart::getTrianglesForQuads() const { + // if we've been asked for our triangulation of the original quads, but we don't yet have them + // then create them now. + if (!trianglesForQuadsAvailable) { + trianglesForQuadsAvailable = true; + + quadsAsTrianglesIndicesBuffer = std::make_shared(); + + // QVector quadIndices; // original indices from the FBX mesh + QVector quadsAsTrianglesIndices; // triangle versions of quads converted when first needed + const int INDICES_PER_ORIGINAL_QUAD = 4; + const int INDICES_PER_TRIANGULATED_QUAD = 6; + int numberOfQuads = quadIndices.size() / INDICES_PER_ORIGINAL_QUAD; + + quadsAsTrianglesIndices.resize(numberOfQuads * INDICES_PER_TRIANGULATED_QUAD); + + int originalIndex = 0; + int triangulatedIndex = 0; + for (int fromQuad = 0; fromQuad < numberOfQuads; fromQuad++) { + int i0 = quadIndices[originalIndex + 0]; + int i1 = quadIndices[originalIndex + 1]; + int i2 = quadIndices[originalIndex + 2]; + int i3 = quadIndices[originalIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + quadsAsTrianglesIndices[triangulatedIndex + 0] = i0; + quadsAsTrianglesIndices[triangulatedIndex + 1] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 2] = i3; + + quadsAsTrianglesIndices[triangulatedIndex + 3] = i1; + quadsAsTrianglesIndices[triangulatedIndex + 4] = i2; + quadsAsTrianglesIndices[triangulatedIndex + 5] = i3; + + originalIndex += INDICES_PER_ORIGINAL_QUAD; + triangulatedIndex += INDICES_PER_TRIANGULATED_QUAD; + } + + trianglesForQuadsIndicesCount = INDICES_PER_TRIANGULATED_QUAD * numberOfQuads; + quadsAsTrianglesIndicesBuffer->append(quadsAsTrianglesIndices.size() * sizeof(quint32), (gpu::Byte*)quadsAsTrianglesIndices.data()); + + } + return quadsAsTrianglesIndicesBuffer; +} + void appendIndex(MeshData& data, QVector& indices, int index) { if (index >= data.polygonIndices.size()) { return; @@ -1089,7 +1139,6 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); - } else { for (int nextIndex = beginIndex + 1;; ) { appendIndex(data, part.triangleIndices, beginIndex); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 198e2d3534..a20dd4a07f 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -109,9 +109,10 @@ public: class FBXMeshPart { public: - QVector quadIndices; - QVector triangleIndices; - + QVector quadIndices; // original indices from the FBX mesh + QVector triangleIndices; // original indices from the FBX mesh + mutable gpu::BufferPointer quadsAsTrianglesIndicesBuffer; + glm::vec3 diffuseColor; glm::vec3 specularColor; glm::vec3 emissiveColor; @@ -126,6 +127,10 @@ public: QString materialID; model::MaterialPointer _material; + mutable bool trianglesForQuadsAvailable = false; + mutable int trianglesForQuadsIndicesCount = 0; + + gpu::BufferPointer getTrianglesForQuads() const; }; /// A single mesh (with optional blendshapes) extracted from an FBX document. diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 1e4f3f7190..b77bafc354 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -2096,8 +2096,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } if (part.quadIndices.size() > 0) { - batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); + batch.setIndexBuffer(gpu::UINT32, part.getTrianglesForQuads(), 0); + batch.drawIndexed(gpu::TRIANGLES, part.trianglesForQuadsIndicesCount, 0); + offset += part.quadIndices.size() * sizeof(int); + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); // restore this in case there are triangles too } if (part.triangleIndices.size() > 0) { From b138c16c7f689e249cdca0436d1bbcd4b52fbf14 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 16:50:36 -0700 Subject: [PATCH 101/109] use Austins QUAD to TRIANGLE_STRIP approach --- libraries/render-utils/src/GeometryCache.cpp | 89 +++----------------- libraries/render-utils/src/GeometryCache.h | 2 - 2 files changed, 14 insertions(+), 77 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 12a6d46be6..3d43cc6934 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1012,27 +1012,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co } const int FLOATS_PER_VERTEX = 2; // vertices - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; - details.streamFormat = streamFormat; details.stream = stream; @@ -1046,8 +1040,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, maxCorner.x, minCorner.y, - maxCorner.x, maxCorner.y, - minCorner.x, maxCorner.y }; + minCorner.x, maxCorner.y, + maxCorner.x, maxCorner.y + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1056,24 +1051,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderUnitCube(gpu::Batch& batch) { @@ -1121,25 +1105,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 2; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; details.streamFormat = streamFormat; details.stream = stream; @@ -1155,8 +1135,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, - maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, - minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y }; + minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; @@ -1166,24 +1147,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) { @@ -1209,26 +1179,22 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co } const int FLOATS_PER_VERTEX = 3; // vertices - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices if (!details.isCreated) { details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; details.streamFormat = streamFormat; details.stream = stream; @@ -1243,8 +1209,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, - maxCorner.x, maxCorner.y, maxCorner.z, - minCorner.x, maxCorner.y, maxCorner.z }; + minCorner.x, maxCorner.y, maxCorner.z, + maxCorner.x, maxCorner.y, maxCorner.z + }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | @@ -1253,24 +1220,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, @@ -1315,7 +1271,6 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } const int FLOATS_PER_VERTEX = 3 + 2; // 3d vertices + text coords - const int NUMBER_OF_INDICES = 6; // 1 quad = 2 triangles const int VERTICES = 4; // 1 quad = 4 vertices const int NUM_POS_COORDS = 3; const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); @@ -1324,20 +1279,15 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.isCreated = true; details.vertices = VERTICES; - details.indices = NUMBER_OF_INDICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto indicesBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; details.colorBuffer = colorBuffer; - details.indicesBuffer = indicesBuffer; - details.streamFormat = streamFormat; details.stream = stream; @@ -1350,9 +1300,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, + topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; @@ -1363,24 +1313,13 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - quint16 indices[NUMBER_OF_INDICES] = { 0, 1, 3, 1, 2, 3 }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - details.indicesBuffer->append(sizeof(indices), (gpu::Byte*) indices); } batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); - batch.setIndexBuffer(gpu::UINT16, details.indicesBuffer, 0); - batch.drawIndexed(gpu::TRIANGLES, NUMBER_OF_INDICES, 0); + batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 19e84e9346..812d12b846 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -235,12 +235,10 @@ private: static int population; gpu::BufferPointer verticesBuffer; gpu::BufferPointer colorBuffer; - gpu::BufferPointer indicesBuffer; gpu::Stream::FormatPointer streamFormat; gpu::BufferStreamPointer stream; int vertices; - int indices; int vertexSize; bool isCreated; From b02f751830bdc9cbd526af7c57608ba1dadd7348 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 16:53:35 -0700 Subject: [PATCH 102/109] diff redux --- libraries/render-utils/src/GeometryCache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3d43cc6934..ca14e80cd3 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1041,7 +1041,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co minCorner.x, minCorner.y, maxCorner.x, minCorner.y, minCorner.x, maxCorner.y, - maxCorner.x, maxCorner.y + maxCorner.x, maxCorner.y, }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; @@ -1136,7 +1136,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, - maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; @@ -1210,7 +1210,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co minCorner.x, minCorner.y, minCorner.z, maxCorner.x, minCorner.y, minCorner.z, minCorner.x, maxCorner.y, maxCorner.z, - maxCorner.x, maxCorner.y, maxCorner.z + maxCorner.x, maxCorner.y, maxCorner.z, }; const int NUM_COLOR_SCALARS_PER_QUAD = 4; From 771ce6dca311ef6ec5be54e0075881b7da1c9d7e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 17:20:48 -0700 Subject: [PATCH 103/109] remove QUAD_STRIP from Circle3DOverlay --- interface/src/ui/overlays/Circle3DOverlay.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 53f1b4ce21..fafb759439 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -119,19 +119,21 @@ void Circle3DOverlay::render(RenderArgs* args) { float angle = startAt; float angleInRadians = glm::radians(angle); - glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); - glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << firstInnerPoint << firstOuterPoint; + glm::vec2 mostRecentInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); + glm::vec2 mostRecentOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); while (angle < endAt) { angleInRadians = glm::radians(angle); glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - points << thisOuterPoint << thisInnerPoint; + points << mostRecentInnerPoint << mostRecentOuterPoint << thisOuterPoint; // first triangle + points << mostRecentInnerPoint << thisInnerPoint << thisOuterPoint; // second triangle angle += SLICE_ANGLE; + + mostRecentInnerPoint = thisInnerPoint; + mostRecentOuterPoint = thisOuterPoint; } // get the last slice portion.... @@ -139,13 +141,14 @@ void Circle3DOverlay::render(RenderArgs* args) { angleInRadians = glm::radians(angle); glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); - - points << lastOuterPoint << lastInnerPoint; + + points << mostRecentInnerPoint << mostRecentOuterPoint << lastOuterPoint; // first triangle + points << mostRecentInnerPoint << lastInnerPoint << lastOuterPoint; // second triangle geometryCache->updateVertices(_quadVerticesID, points, color); } - geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID); + geometryCache->renderVertices(batch, gpu::TRIANGLES, _quadVerticesID); } else { if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { From 493836e36312647ab5fa25313e9937ed704000f7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Aug 2015 17:26:38 -0700 Subject: [PATCH 104/109] remove QUADS from GLBackendShared and Format --- libraries/gpu/src/gpu/Format.h | 5 ----- libraries/gpu/src/gpu/GLBackendShared.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 01d3f37ef8..b710a44f39 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -213,11 +213,6 @@ enum Primitive { TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, - - // FIXME - remove these - QUADS, - QUAD_STRIP, - NUM_PRIMITIVES, }; diff --git a/libraries/gpu/src/gpu/GLBackendShared.h b/libraries/gpu/src/gpu/GLBackendShared.h index 888fd1164d..7ce54665be 100644 --- a/libraries/gpu/src/gpu/GLBackendShared.h +++ b/libraries/gpu/src/gpu/GLBackendShared.h @@ -23,8 +23,6 @@ static const GLenum _primitiveToGLmode[gpu::NUM_PRIMITIVES] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, - GL_QUADS, - GL_QUAD_STRIP, }; static const GLenum _elementTypeToGLType[gpu::NUM_TYPES] = { From 8f616c04fb08154b6b4f4dee5a78131c57e75cd8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 2 Aug 2015 21:46:39 -0700 Subject: [PATCH 105/109] Fix https://app.asana.com/0/32622044445063/43589567731045 --- libraries/audio-client/src/AudioClient.cpp | 17 ++++++++--------- libraries/audio-client/src/AudioClient.h | 3 --- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 908310d504..a3d3a72043 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1037,9 +1037,14 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { localOutput->moveToThread(injector->getLocalBuffer()->thread()); // have it be stopped when that local buffer is about to close - connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged); - connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop); - connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop); + // We don't want to stop this localOutput and injector whenever this AudioClient singleton goes idle, + // only when the localOutput does. But the connection is to localOutput, so that it happens on the right thread. + connect(localOutput, &QAudioOutput::stateChanged, localOutput, [=](QAudio::State state) { + if (state == QAudio::IdleState) { + localOutput->stop(); + injector->stop(); + } + }); connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); @@ -1358,9 +1363,3 @@ void AudioClient::saveSettings() { windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction()); repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade()); } - -void AudioClient::audioStateChanged(QAudio::State state) { - if (state == QAudio::IdleState) { - emit audioFinished(); - } -} diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index a1d08ec540..3205aeda1d 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -209,9 +209,6 @@ protected: deleteLater(); } -private slots: - void audioStateChanged(QAudio::State state); - private: void outputFormatChanged(); From 055c9dc59b5e064bfde03281dd894148cf487a21 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 10:41:35 -0700 Subject: [PATCH 106/109] Fix image URLs in controlPanel.js. --- examples/controlPanel.js | 8 ++++---- examples/libraries/overlayManager.js | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index 837b59ffa0..ebad7d8d83 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -17,10 +17,10 @@ Script.include([ ]); var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg"; -var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/close.svg"; -var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/mic-toggle.svg"; -var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/face-toggle.svg"; -var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/address-bar-toggle.svg"; +var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/close.svg"; +var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg"; +var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg"; +var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg"; var panel = new FloatingUIPanel({ anchorPosition: { diff --git a/examples/libraries/overlayManager.js b/examples/libraries/overlayManager.js index 55575badee..cd05cd2a52 100644 --- a/examples/libraries/overlayManager.js +++ b/examples/libraries/overlayManager.js @@ -24,6 +24,10 @@ // // See more on usage below. // +// Note that including this file will delete Overlays from the global scope. All the +// functionality of Overlays is represented here, just better. If you try to use Overlays in +// tandem, there may be performance problems or nasty surprises. +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // From d24006e71593ae6db7d03b921d6b6dca011eb462 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 11:14:55 -0700 Subject: [PATCH 107/109] Improve logic around handling click and drag. --- examples/controlPanel.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/controlPanel.js b/examples/controlPanel.js index ebad7d8d83..59d66fdaf9 100644 --- a/examples/controlPanel.js +++ b/examples/controlPanel.js @@ -172,26 +172,25 @@ function onFaceMuteToggled() { } onFaceMuteToggled(); -var isLeftClick = false, - isRightClick = false; +var mouseDown = {}; function onMouseDown(event) { - isLeftClick = event.isLeftButton; - isRightClick = event.isRightButton; -} - -function onMouseMove(event) { - isLeftClick = isRightClick = false; + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } } function onMouseUp(event) { - if (isLeftClick && event.isLeftButton) { + if (event.isLeftButton) { var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); - if (overlay && overlay.onClick) { + if (overlay && overlay === mouseDown.overlay && overlay.onClick) { overlay.onClick(event); } } - if (isRightClick && event.isRightButton) { + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { panel.setProperties({ visible: !panel.visible, offsetRotation: { @@ -200,7 +199,8 @@ function onMouseUp(event) { } }); } - isLeftClick = isRightClick = false; + + mouseDown = {}; } function onScriptEnd(event) { @@ -208,7 +208,6 @@ function onScriptEnd(event) { } Controller.mousePressEvent.connect(onMouseDown); -Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); AudioDevice.muteToggled.connect(onMicMuteToggled); FaceTracker.muteToggled.connect(onFaceMuteToggled); From 1b4ba75b5a597313576940810833cb5f2a59d82c Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 3 Aug 2015 12:01:15 -0700 Subject: [PATCH 108/109] Fix click and drag logic for floatingUIExample.js. --- examples/example/ui/floatingUIExample.js | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/examples/example/ui/floatingUIExample.js b/examples/example/ui/floatingUIExample.js index 9c4e43be94..1e957fe6b3 100644 --- a/examples/example/ui/floatingUIExample.js +++ b/examples/example/ui/floatingUIExample.js @@ -127,35 +127,37 @@ blueSquare3.offsetPosition = { }; -function onMouseDown(event) { - isLeftClick = event.isLeftButton; - isRightClick = event.isRightButton; -} +var mouseDown = {}; -function onMouseMove(event) { - isLeftClick = isRightClick = false; +function onMouseDown(event) { + if (event.isLeftButton) { + mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); + } + if (event.isRightButton) { + mouseDown.pos = { x: event.x, y: event.y }; + } } function onMouseUp(event) { - if (isLeftClick && event.isLeftButton) { + if (event.isLeftButton) { var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y }); - print(overlay.attachedPanel); - if (overlay.attachedPanel === bluePanel) { - overlay.destroy(); - } else if (overlay) { - var oldPos = overlay.offsetPosition; - var newPos = { - x: Number(oldPos.x), - y: Number(oldPos.y), - z: Number(oldPos.z) + 0.1 - }; - overlay.offsetPosition = newPos; + if (overlay === mouseDown.overlay) { + if (overlay.attachedPanel === bluePanel) { + overlay.destroy(); + } else if (overlay) { + var oldPos = overlay.offsetPosition; + var newPos = { + x: Number(oldPos.x), + y: Number(oldPos.y), + z: Number(oldPos.z) + 0.1 + }; + overlay.offsetPosition = newPos; + } } } - if (isRightClick && event.isRightButton) { + if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) { mainPanel.visible = !mainPanel.visible; } - isLeftClick = isRightClick = false; } function onScriptEnd() { @@ -163,6 +165,5 @@ function onScriptEnd() { } Controller.mousePressEvent.connect(onMouseDown); -Controller.mouseMoveEvent.connect(onMouseMove); Controller.mouseReleaseEvent.connect(onMouseUp); Script.scriptEnding.connect(onScriptEnd); \ No newline at end of file From 5e7cb728438b4ce6391c3241251e129249046de0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Aug 2015 13:02:12 -0700 Subject: [PATCH 109/109] Reduce the speed of movement when in HMD view When using hmdControls.js. --- examples/hmdControls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hmdControls.js b/examples/hmdControls.js index e14ddca3ef..04c1dade0a 100644 --- a/examples/hmdControls.js +++ b/examples/hmdControls.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var MOVE_DISTANCE = 10.0; +var MOVE_DISTANCE = 2.0; var PITCH_INCREMENT = 0.5; // degrees var pitchChange = 0; // degrees var YAW_INCREMENT = 0.5; // degrees