diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d234c574d1..146bc72fcf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3976,7 +3976,7 @@ void Application::updateMyAvatarLookAtPosition() { if (faceAngle < MAXIMUM_FACE_ANGLE) { // Randomly look back and forth between look targets eyeContactTarget target = Menu::getInstance()->isOptionChecked(MenuOption::FixGaze) ? - LEFT_EYE : myAvatar->getEyeContactTarget(); + LEFT_EYE : myAvatar->getEyeContactTarget(); switch (target) { case LEFT_EYE: lookAtSpot = lookingAtHead->getLeftEyePosition(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d47e4cfd10..20b3949bc6 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -52,7 +52,7 @@ const QUuid MY_AVATAR_KEY; // NULL key AvatarManager::AvatarManager(QObject* parent) : _avatarsToFade(), - _myAvatar(std::make_shared(qApp->thread(), std::make_shared())) + _myAvatar(std::make_shared(qApp->thread())) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); @@ -300,7 +300,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return std::make_shared(qApp->thread(), std::make_shared()); + return std::make_shared(qApp->thread()); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9cf8e7747b..5e513a9d9f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -98,8 +98,8 @@ static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.91 static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f }; -MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : - Avatar(thread, rig), +MyAvatar::MyAvatar(QThread* thread) : + Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), @@ -120,7 +120,6 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : _goToPending(false), _goToPosition(), _goToOrientation(), - _rig(rig), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()) @@ -129,7 +128,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : // give the pointer to our head to inherited _headData variable from AvatarData _headData = new MyHead(this); - _skeletonModel = std::make_shared(this, nullptr, rig); + _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); @@ -180,9 +179,7 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : auto audioIO = DependencyManager::get(); audioIO->setIsPlayingBackRecording(isPlaying); - if (_rig) { - _rig->setEnableAnimations(!isPlaying); - } + _skeletonModel->getRig().setEnableAnimations(!isPlaying); }); connect(recorder.data(), &Recorder::recordingStateChanged, [=] { @@ -233,12 +230,12 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : } auto jointData = dummyAvatar.getRawJointData(); - if (jointData.length() > 0 && _rig) { - _rig->copyJointsFromJointData(jointData); + if (jointData.length() > 0) { + _skeletonModel->getRig().copyJointsFromJointData(jointData); } }); - connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); + connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); _characterController.setDensity(_density); } @@ -355,9 +352,7 @@ void MyAvatar::clearIKJointLimitHistory() { return; } - if (_rig) { - _rig->clearIKJointLimitHistory(); - } + _skeletonModel->getRig().clearIKJointLimitHistory(); } void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { @@ -528,10 +523,8 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("skeleton"); - if (_rig) { - _rig->setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets); - _rig->setEnableDebugDrawIKConstraints(_enableDebugDrawIKConstraints); - } + _skeletonModel->getRig().setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets); + _skeletonModel->getRig().setEnableDebugDrawIKConstraints(_enableDebugDrawIKConstraints); _skeletonModel->simulate(deltaTime); } @@ -550,7 +543,7 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model if (_rigEnabled) { - _rig->copyJointsIntoJointData(_jointData); + _skeletonModel->getRig().copyJointsIntoJointData(_jointData); } } @@ -795,7 +788,7 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); return; } - _rig->overrideAnimation(url, fps, loop, firstFrame, lastFrame); + _skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame); } void MyAvatar::restoreAnimation() { @@ -803,7 +796,7 @@ void MyAvatar::restoreAnimation() { QMetaObject::invokeMethod(this, "restoreAnimation"); return; } - _rig->restoreAnimation(); + _skeletonModel->getRig().restoreAnimation(); } QStringList MyAvatar::getAnimationRoles() { @@ -812,7 +805,7 @@ QStringList MyAvatar::getAnimationRoles() { QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result)); return result; } - return _rig->getAnimationRoles(); + return _skeletonModel->getRig().getAnimationRoles(); } void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, @@ -822,7 +815,7 @@ void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, fl Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); return; } - _rig->overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame); + _skeletonModel->getRig().overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame); } void MyAvatar::restoreRoleAnimation(const QString& role) { @@ -830,7 +823,7 @@ void MyAvatar::restoreRoleAnimation(const QString& role) { QMetaObject::invokeMethod(this, "restoreRoleAnimation", Q_ARG(const QString&, role)); return; } - _rig->restoreRoleAnimation(role); + _skeletonModel->getRig().restoreRoleAnimation(role); } void MyAvatar::saveData() { @@ -968,7 +961,7 @@ void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { - _rig->setEnableInverseKinematics(isEnabled); + _skeletonModel->getRig().setEnableInverseKinematics(isEnabled); } void MyAvatar::loadData() { @@ -1217,7 +1210,7 @@ void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec return; } // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); + _skeletonModel->getRig().setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); } void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { @@ -1226,7 +1219,7 @@ void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { return; } // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); + _skeletonModel->getRig().setJointRotation(index, true, rotation, SCRIPT_PRIORITY); } void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { @@ -1235,7 +1228,7 @@ void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { return; } // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); + _skeletonModel->getRig().setJointTranslation(index, true, translation, SCRIPT_PRIORITY); } void MyAvatar::clearJointData(int index) { @@ -1243,7 +1236,7 @@ void MyAvatar::clearJointData(int index) { QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); return; } - _rig->clearJointAnimationPriority(index); + _skeletonModel->getRig().clearJointAnimationPriority(index); } void MyAvatar::clearJointsData() { @@ -1251,7 +1244,7 @@ void MyAvatar::clearJointsData() { QMetaObject::invokeMethod(this, "clearJointsData"); return; } - _rig->clearJointStates(); + _skeletonModel->getRig().clearJointStates(); } void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { @@ -1694,7 +1687,7 @@ void MyAvatar::setAnimGraphUrl(const QUrl& url) { _skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render. _currentAnimGraphUrl.set(url); - _rig->initAnimGraph(url); + _skeletonModel->getRig().initAnimGraph(url); _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes @@ -1710,7 +1703,7 @@ void MyAvatar::initAnimGraph() { graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json"); } - _rig->initAnimGraph(graphUrl); + _skeletonModel->getRig().initAnimGraph(graphUrl); _currentAnimGraphUrl.set(graphUrl); _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. @@ -1718,7 +1711,7 @@ void MyAvatar::initAnimGraph() { } void MyAvatar::destroyAnimGraph() { - _rig->destroyAnimGraph(); + _skeletonModel->getRig().destroyAnimGraph(); } void MyAvatar::postUpdate(float deltaTime) { @@ -1734,22 +1727,23 @@ void MyAvatar::postUpdate(float deltaTime) { if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { - auto animSkeleton = _rig->getAnimSkeleton(); + auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton(); // the rig is in the skeletonModel frame AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation()); if (_enableDebugDrawDefaultPose && animSkeleton) { glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); - AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _rig->getAbsoluteDefaultPoses(), xform, gray); + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _skeletonModel->getRig().getAbsoluteDefaultPoses(), xform, gray); } if (_enableDebugDrawAnimPose && animSkeleton) { // build absolute AnimPoseVec from rig AnimPoseVec absPoses; - absPoses.reserve(_rig->getJointStateCount()); - for (int i = 0; i < _rig->getJointStateCount(); i++) { - absPoses.push_back(AnimPose(_rig->getJointTransform(i))); + const Rig& rig = _skeletonModel->getRig(); + absPoses.reserve(rig.getJointStateCount()); + for (int i = 0; i < rig.getJointStateCount(); i++) { + absPoses.push_back(AnimPose(rig.getJointTransform(i))); } glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan); @@ -2338,17 +2332,18 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - int rightEyeIndex = _rig->indexOfJoint("RightEye"); - int leftEyeIndex = _rig->indexOfJoint("LeftEye"); - int neckIndex = _rig->indexOfJoint("Neck"); - int hipsIndex = _rig->indexOfJoint("Hips"); + const Rig& rig = _skeletonModel->getRig(); + int rightEyeIndex = rig.indexOfJoint("RightEye"); + int leftEyeIndex = rig.indexOfJoint("LeftEye"); + int neckIndex = rig.indexOfJoint("Neck"); + int hipsIndex = rig.indexOfJoint("Hips"); glm::vec3 rigMiddleEyePos = DEFAULT_AVATAR_MIDDLE_EYE_POS; if (leftEyeIndex >= 0 && rightEyeIndex >= 0) { - rigMiddleEyePos = (_rig->getAbsoluteDefaultPose(leftEyeIndex).trans() + _rig->getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f; + rigMiddleEyePos = (rig.getAbsoluteDefaultPose(leftEyeIndex).trans() + rig.getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f; } - glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS; - glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS; + glm::vec3 rigNeckPos = neckIndex != -1 ? rig.getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS; + glm::vec3 rigHipsPos = hipsIndex != -1 ? rig.getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS; glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos); glm::vec3 localNeck = (rigNeckPos - rigHipsPos); @@ -2714,8 +2709,8 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const { glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int rightEyeIndex = _rig->indexOfJoint("RightEye"); - int leftEyeIndex = _rig->indexOfJoint("LeftEye"); + int rightEyeIndex = _skeletonModel->getRig().indexOfJoint("RightEye"); + int leftEyeIndex = _skeletonModel->getRig().indexOfJoint("LeftEye"); if (rightEyeIndex >= 0 && leftEyeIndex >= 0) { auto centerEyePos = (getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex) + getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)) * 0.5f; auto centerEyeRot = Quaternions::Y_180; @@ -2727,7 +2722,7 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const { glm::mat4 MyAvatar::getHeadCalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int headIndex = _rig->indexOfJoint("Head"); + int headIndex = _skeletonModel->getRig().indexOfJoint("Head"); if (headIndex >= 0) { auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex); auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex); @@ -2739,7 +2734,7 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const { glm::mat4 MyAvatar::getSpine2CalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int spine2Index = _rig->indexOfJoint("Spine2"); + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); if (spine2Index >= 0) { auto spine2Pos = getAbsoluteDefaultJointTranslationInObjectFrame(spine2Index); auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index); @@ -2751,7 +2746,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const { glm::mat4 MyAvatar::getHipsCalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int hipsIndex = _rig->indexOfJoint("Hips"); + int hipsIndex = _skeletonModel->getRig().indexOfJoint("Hips"); if (hipsIndex >= 0) { auto hipsPos = getAbsoluteDefaultJointTranslationInObjectFrame(hipsIndex); auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex); @@ -2763,7 +2758,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const { glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int leftFootIndex = _rig->indexOfJoint("LeftFoot"); + int leftFootIndex = _skeletonModel->getRig().indexOfJoint("LeftFoot"); if (leftFootIndex >= 0) { auto leftFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftFootIndex); auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex); @@ -2775,7 +2770,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { glm::mat4 MyAvatar::getRightFootCalibrationMat() const { // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. - int rightFootIndex = _rig->indexOfJoint("RightFoot"); + int rightFootIndex = _skeletonModel->getRig().indexOfJoint("RightFoot"); if (rightFootIndex >= 0) { auto rightFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightFootIndex); auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex); @@ -2795,7 +2790,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o slamPosition(position); setOrientation(orientation); - _rig->setMaxHipsOffsetLength(0.05f); + _skeletonModel->getRig().setMaxHipsOffsetLength(0.05f); auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); if (it == _pinnedJoints.end()) { @@ -2812,7 +2807,7 @@ bool MyAvatar::clearPinOnJoint(int index) { auto hipsIndex = getJointIndex("Hips"); if (index == hipsIndex) { - _rig->setMaxHipsOffsetLength(FLT_MAX); + _skeletonModel->getRig().setMaxHipsOffsetLength(FLT_MAX); } return true; @@ -2821,7 +2816,7 @@ bool MyAvatar::clearPinOnJoint(int index) { } float MyAvatar::getIKErrorOnLastSolve() const { - return _rig->getIKErrorOnLastSolve(); + return _skeletonModel->getRig().getIKErrorOnLastSolve(); } // thread-safe diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 394d6b2ac7..f386d8d345 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -148,7 +148,7 @@ public: }; Q_ENUM(DriveKeys) - explicit MyAvatar(QThread* thread, RigPointer rig); + explicit MyAvatar(QThread* thread); ~MyAvatar(); void instantiableAvatar() override {}; @@ -322,9 +322,9 @@ public: // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. // It is not specified in what order multiple handlers are called. - Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); } + Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); } // Removes a handler previously added by addAnimationStateHandler. - Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } + Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _skeletonModel->getRig().removeAnimationStateHandler(handler); } Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; } Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; } @@ -708,7 +708,6 @@ private: glm::quat _goToOrientation; std::unordered_set _headBoneSet; - RigPointer _rig; bool _prevShouldDrawHead; bool _rigEnabled { true }; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 793fbb79c4..f02aefec5b 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -44,17 +44,14 @@ glm::quat MyHead::getCameraOrientation() const { void MyHead::simulate(float deltaTime) { auto player = DependencyManager::get(); // Only use face trackers when not playing back a recording. - if (player->isPlaying()) { - Parent::simulate(deltaTime); - } else { - computeAudioLoudness(deltaTime); - + if (!player->isPlaying()) { FaceTracker* faceTracker = qApp->getActiveFaceTracker(); - _isFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); + _isFaceTrackerConnected = faceTracker != nullptr && !faceTracker->isMuted(); if (_isFaceTrackerConnected) { _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { calculateMouthShapes(deltaTime); @@ -71,19 +68,9 @@ void MyHead::simulate(float deltaTime) { } applyEyelidOffset(getFinalOrientationInWorldFrame()); } - } else { - computeFaceMovement(deltaTime); - } - - auto eyeTracker = DependencyManager::get(); - _isEyeTrackerConnected = eyeTracker && eyeTracker->isTracking(); - if (_isEyeTrackerConnected) { - // TODO? figure out where EyeTracker data harvested. Move it here? - _saccade = glm::vec3(); - } else { - computeEyeMovement(deltaTime); } - + auto eyeTracker = DependencyManager::get(); + _isEyeTrackerConnected = eyeTracker->isTracking(); } - computeEyePosition(); + Parent::simulate(deltaTime); } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index e60481fc62..828a5f8a01 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -13,7 +13,7 @@ #include "Application.h" #include "InterfaceLogging.h" -MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel(owningAvatar, parent, rig) { +MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) { } Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { @@ -63,7 +63,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); glm::mat4 worldToRig = glm::inverse(rigToWorld); glm::mat4 rigHMDMat = worldToRig * worldHMDMat; - _rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); + _rig.computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); headParams.headEnabled = true; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. @@ -94,7 +94,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; - _rig->updateFromHeadParameters(headParams, deltaTime); + _rig.updateFromHeadParameters(headParams, deltaTime); Rig::HandAndFeetParameters handAndFeetParams; @@ -138,14 +138,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); - _rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); + _rig.updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); auto velocity = myAvatar->getLocalVelocity(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); - _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); + _rig.computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); @@ -158,6 +158,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - _rig->updateFromEyeParameters(eyeParams); + _rig.updateFromEyeParameters(eyeParams); } diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index 84fccc825a..12aba6b545 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -19,7 +19,7 @@ private: using Parent = SkeletonModel; public: - MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); + MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr); void updateRig(float deltaTime, glm::mat4 parentTransform) override; }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 307f23bff3..0bed07891e 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -18,7 +18,7 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(std::make_shared(std::make_shared(), nullptr, this)), + : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()) { _model->init(); @@ -28,7 +28,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(std::make_shared(std::make_shared(), nullptr, this)), + _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false), @@ -211,12 +211,10 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "jointNames") { if (_model && _model->isActive()) { // note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty - const RigPointer rig = _model->getRig(); - if (rig) { - return mapJoints([rig](int jointIndex) -> QString { - return rig->nameOfJoint(jointIndex); - }); - } + const Rig* rig = &(_model->getRig()); + return mapJoints([rig](int jointIndex) -> QString { + return rig->nameOfJoint(jointIndex); + }); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 33b66f91ea..c6d4073d9e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -27,7 +27,6 @@ class Rig; class AnimInverseKinematics; -typedef std::shared_ptr RigPointer; // Rig instances are reentrant. // However only specific methods thread-safe. Noted below. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 1968a731a4..f5e2c8b68d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -98,7 +98,7 @@ void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } -Avatar::Avatar(QThread* thread, RigPointer rig) : +Avatar::Avatar(QThread* thread) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread @@ -344,9 +344,9 @@ void Avatar::simulate(float deltaTime, bool inView) { if (inView) { Head* head = getHead(); if (_hasNewJointData) { - _skeletonModel->getRig()->copyJointsFromJointData(_jointData); + _skeletonModel->getRig().copyJointsFromJointData(_jointData); glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); - _skeletonModel->getRig()->computeExternalPoses(rootTransform); + _skeletonModel->getRig().computeExternalPoses(rootTransform); _jointDataSimulationRate.increment(); _skeletonModel->simulate(deltaTime, true); @@ -907,17 +907,16 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const { glm::quat rotation; - auto rig = _skeletonModel->getRig(); - glm::quat rot = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).rot(); + glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot(); return Quaternions::Y_180 * rot; } glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const { glm::vec3 translation; - auto rig = _skeletonModel->getRig(); - glm::vec3 trans = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); + const Rig& rig = _skeletonModel->getRig(); + glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - return transformPoint(y180Mat * rig->getGeometryToRigTransform(), trans); + return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans); } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { @@ -1083,16 +1082,16 @@ void Avatar::setModelURLFinished(bool success) { // create new model, can return an instance of a SoftAttachmentModel rather then Model -static std::shared_ptr allocateAttachmentModel(bool isSoft, RigPointer rigOverride, bool isCauterized) { +static std::shared_ptr allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) { if (isSoft) { // cast to std::shared_ptr - std::shared_ptr softModel = std::make_shared(std::make_shared(), nullptr, rigOverride); + std::shared_ptr softModel = std::make_shared(nullptr, rigOverride); if (isCauterized) { softModel->flagAsCauterized(); } return std::dynamic_pointer_cast(softModel); } else { - return std::make_shared(std::make_shared()); + return std::make_shared(); } } @@ -1409,21 +1408,19 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { QList Avatar::getSkeleton() { SkeletonModelPointer skeletonModel = _skeletonModel; if (skeletonModel) { - RigPointer rig = skeletonModel->getRig(); - if (rig) { - AnimSkeleton::ConstPointer skeleton = rig->getAnimSkeleton(); - if (skeleton) { - QList list; - list.reserve(skeleton->getNumJoints()); - for (int i = 0; i < skeleton->getNumJoints(); i++) { - QVariantMap obj; - obj["name"] = skeleton->getJointName(i); - obj["index"] = i; - obj["parentIndex"] = skeleton->getParentIndex(i); - list.push_back(obj); - } - return list; + const Rig& rig = skeletonModel->getRig(); + AnimSkeleton::ConstPointer skeleton = rig.getAnimSkeleton(); + if (skeleton) { + QList list; + list.reserve(skeleton->getNumJoints()); + for (int i = 0; i < skeleton->getNumJoints(); i++) { + QVariantMap obj; + obj["name"] = skeleton->getJointName(i); + obj["index"] = i; + obj["parentIndex"] = skeleton->getParentIndex(i); + list.push_back(obj); } + return list; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index ae24caca29..1724d42510 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -75,7 +75,7 @@ public: static void setShowCollisionShapes(bool render); static void setShowNamesAboveHeads(bool show); - explicit Avatar(QThread* thread, RigPointer rig = nullptr); + explicit Avatar(QThread* thread); ~Avatar(); virtual void instantiableAvatar() = 0; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index b4b0929c0c..96ecd86ff4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -23,8 +23,6 @@ #include "Avatar.h" -const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for - using namespace std; static bool disableEyelidAdjustment { false }; @@ -43,7 +41,9 @@ void Head::reset() { _baseYaw = _basePitch = _baseRoll = 0.0f; } -void Head::computeAudioLoudness(float deltaTime) { +void Head::simulate(float deltaTime) { + const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for + // grab the audio loudness from the owning avatar, if we have one float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f; @@ -58,99 +58,102 @@ void Head::computeAudioLoudness(float deltaTime) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } - float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz - _audioAttack = audioAttackAveragingRate * _audioAttack + - (1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness); - _lastLoudness = (audioLoudness - _longTermAverageLoudness); -} + if (!_isFaceTrackerConnected) { + if (!_isEyeTrackerConnected) { + // 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; -void Head::computeEyeMovement(float deltaTime) { - // 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); + } else { + _saccade = glm::vec3(); + } - 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; + _timeWithoutTalking += deltaTime; + if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { + _timeWithoutTalking = 0.0f; + } else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) { + forceBlink = true; + } - // 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; - _timeWithoutTalking += deltaTime; - if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) { - _timeWithoutTalking = 0.0f; - } else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) { - forceBlink = true; - } + // Update audio attack data for facial animation (eyebrows and mouth) + float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz + _audioAttack = audioAttackAveragingRate * _audioAttack + + (1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness); + _lastLoudness = (audioLoudness - _longTermAverageLoudness); - 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; + 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(deltaTime); + FaceTracker::updateFakeCoefficients(_leftEyeBlink, + _rightEyeBlink, + _browAudioLift, + _audioJawOpen, + _mouth2, + _mouth3, + _mouth4, + _transientBlendshapeCoefficients); + + applyEyelidOffset(getOrientation()); + } 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; - } + _saccade = glm::vec3(); } - applyEyelidOffset(getOrientation()); -} - -void Head::computeFaceMovement(float deltaTime) { - // Update audio attack data for facial animation (eyebrows and mouth) - 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); - - // use data to update fake Faceshift blendshape coefficients - calculateMouthShapes(deltaTime); - FaceTracker::updateFakeCoefficients(_leftEyeBlink, - _rightEyeBlink, - _browAudioLift, - _audioJawOpen, - _mouth2, - _mouth3, - _mouth4, - _transientBlendshapeCoefficients); -} - -void Head::computeEyePosition() { _leftEyePosition = _rightEyePosition = getPosition(); if (_owningAvatar) { auto skeletonModel = static_cast(_owningAvatar)->getSkeletonModel(); @@ -161,13 +164,6 @@ void Head::computeEyePosition() { _eyePosition = 0.5f * (_leftEyePosition + _rightEyePosition); } -void Head::simulate(float deltaTime) { - computeAudioLoudness(deltaTime); - computeFaceMovement(deltaTime); - computeEyeMovement(deltaTime); - computeEyePosition(); -} - void Head::calculateMouthShapes(float deltaTime) { const float JAW_OPEN_SCALE = 0.015f; const float JAW_OPEN_RATE = 0.9f; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.h b/libraries/avatars-renderer/src/avatars-renderer/Head.h index 39331500b5..c5902285b9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.h @@ -83,11 +83,6 @@ public: float getTimeWithoutTalking() const { return _timeWithoutTalking; } protected: - void computeAudioLoudness(float deltaTime); - void computeEyeMovement(float deltaTime); - void computeFaceMovement(float deltaTime); - void computeEyePosition(); - // disallow copies of the Head, copy of owning Avatar is disallowed too Head(const Head&); Head& operator= (const Head&); diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp index ad69ba24cb..e870e2de12 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp @@ -8,9 +8,9 @@ #include "OtherAvatar.h" -OtherAvatar::OtherAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig) { +OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = new Head(this); - _skeletonModel = std::make_shared(this, nullptr, rig); + _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h index 22a7e1863a..df09d7fd99 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h @@ -13,7 +13,7 @@ class OtherAvatar : public Avatar { public: - explicit OtherAvatar(QThread* thread, RigPointer rig = nullptr); + explicit OtherAvatar(QThread* thread); virtual void instantiableAvatar() override {}; }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index d3453280ac..2a2817e68b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -22,8 +22,8 @@ #include "Avatar.h" #include "Logging.h" -SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : - CauterizedModel(rig, parent), +SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : + CauterizedModel(parent), _owningAvatar(owningAvatar), _boundingCapsuleLocalOffset(0.0f), _boundingCapsuleRadius(0.0f), @@ -31,7 +31,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r _defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)), _headClipDistance(DEFAULT_NEAR_CLIP) { - assert(_rig); assert(_owningAvatar); } @@ -41,12 +40,12 @@ SkeletonModel::~SkeletonModel() { void SkeletonModel::initJointStates() { const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig->initJointStates(geometry, modelOffset); + _rig.initJointStates(geometry, modelOffset); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = geometry.headJointIndex; - if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) { - qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); + if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) { + qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -102,7 +101,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; - _rig->getJointRotation(geometry.headJointIndex, headOrientation); + _rig.getJointRotation(geometry.headJointIndex, headOrientation); glm::vec3 eulers = safeEulerAngles(headOrientation); head->setBasePitch(glm::degrees(-eulers.x)); head->setBaseYaw(glm::degrees(eulers.y)); @@ -116,7 +115,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - _rig->updateFromEyeParameters(eyeParams); + _rig.updateFromEyeParameters(eyeParams); } void SkeletonModel::updateAttitude() { @@ -136,7 +135,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { // let rig compute the model offset glm::vec3 registrationPoint; - if (_rig->getModelRegistrationPoint(registrationPoint)) { + if (_rig.getModelRegistrationPoint(registrationPoint)) { setOffset(registrationPoint); } } else { @@ -164,8 +163,8 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) { } bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const { - int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1"); - int handIndex = _rig->indexOfJoint("LeftHand"); + int knuckleIndex = _rig.indexOfJoint("LeftHandMiddle1"); + int handIndex = _rig.indexOfJoint("LeftHand"); if (knuckleIndex >= 0 && handIndex >= 0) { glm::quat handRotation; glm::vec3 knucklePosition; @@ -189,8 +188,8 @@ bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const { } bool SkeletonModel::getRightGrabPosition(glm::vec3& position) const { - int knuckleIndex = _rig->indexOfJoint("RightHandMiddle1"); - int handIndex = _rig->indexOfJoint("RightHand"); + int knuckleIndex = _rig.indexOfJoint("RightHandMiddle1"); + int handIndex = _rig.indexOfJoint("RightHand"); if (knuckleIndex >= 0 && handIndex >= 0) { glm::quat handRotation; glm::vec3 knucklePosition; @@ -304,7 +303,7 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::computeBoundingShape() { - if (!isLoaded() || _rig->jointStatesEmpty()) { + if (!isLoaded() || _rig.jointStatesEmpty()) { return; } @@ -316,7 +315,7 @@ void SkeletonModel::computeBoundingShape() { float radius, height; glm::vec3 offset; - _rig->computeAvatarBoundingCapsule(geometry, radius, height, offset); + _rig.computeAvatarBoundingCapsule(geometry, radius, height, offset); float invScale = 1.0f / _owningAvatar->getUniformScale(); _boundingCapsuleRadius = invScale * radius; _boundingCapsuleHeight = invScale * height; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 059dd245fd..db87a37477 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -28,7 +28,7 @@ class SkeletonModel : public CauterizedModel { public: - SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); + SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr); ~SkeletonModel(); void initJointStates() override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3aa5ab69fa..d82068b8ac 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2030,17 +2030,6 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { version = JSON_AVATAR_JOINT_ROTATIONS_IN_RELATIVE_FRAME_VERSION; } - // The head setOrientation likes to overwrite the avatar orientation, - // so lets do the head first - // Most head data is relative to the avatar, and needs no basis correction, - // but the lookat vector does need correction - if (json.contains(JSON_AVATAR_HEAD)) { - if (!_headData) { - _headData = new HeadData(this); - } - _headData->fromJson(json[JSON_AVATAR_HEAD].toObject()); - } - if (json.contains(JSON_AVATAR_BODY_MODEL)) { auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString(); if (useFrameSkeleton && bodyModelURL != getSkeletonModelURL().toString()) { @@ -2079,6 +2068,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setOrientation(currentBasis->getRotation()); } + // Do after avatar orientation because head look-at needs avatar orientation. + if (json.contains(JSON_AVATAR_HEAD)) { + if (!_headData) { + _headData = new HeadData(this); + } + _headData->fromJson(json[JSON_AVATAR_HEAD].toObject()); + } + if (json.contains(JSON_AVATAR_SCALE)) { setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); } diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 2704b6539c..8ae33a1b4f 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -52,6 +52,13 @@ glm::quat HeadData::getOrientation() const { return _owningAvatar->getOrientation() * getRawOrientation(); } +void HeadData::setHeadOrientation(const glm::quat& orientation) { + glm::quat bodyOrientation = _owningAvatar->getOrientation(); + glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation)); + _basePitch = eulers.x; + _baseYaw = eulers.y; + _baseRoll = eulers.z; +} void HeadData::setOrientation(const glm::quat& orientation) { // rotate body about vertical axis @@ -61,10 +68,7 @@ void HeadData::setOrientation(const glm::quat& orientation) { _owningAvatar->setOrientation(bodyOrientation); // the rest goes to the head - glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation)); - _basePitch = eulers.x; - _baseYaw = eulers.y; - _baseRoll = eulers.z; + setHeadOrientation(orientation); } //Lazily construct a lookup map from the blendshapes @@ -173,14 +177,14 @@ void HeadData::fromJson(const QJsonObject& json) { } } - if (json.contains(JSON_AVATAR_HEAD_ROTATION)) { - setOrientation(quatFromJsonValue(json[JSON_AVATAR_HEAD_ROTATION])); - } - if (json.contains(JSON_AVATAR_HEAD_LOOKAT)) { auto relativeLookAt = vec3FromJsonValue(json[JSON_AVATAR_HEAD_LOOKAT]); if (glm::length2(relativeLookAt) > 0.01f) { setLookAtPosition((_owningAvatar->getOrientation() * relativeLookAt) + _owningAvatar->getPosition()); } } + + if (json.contains(JSON_AVATAR_HEAD_ROTATION)) { + setHeadOrientation(quatFromJsonValue(json[JSON_AVATAR_HEAD_ROTATION])); + } } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index be9d54e93e..0bb38c1dad 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -101,6 +101,8 @@ private: // privatize copy ctor and assignment operator so copies of this object cannot be made HeadData(const HeadData&); HeadData& operator= (const HeadData&); + + void setHeadOrientation(const glm::quat& orientation); }; #endif // hifi_HeadData_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1de476c825..09308baabb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -582,7 +582,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading return model; } - model = std::make_shared(std::make_shared(), nullptr, spatiallyNestableOverride); + model = std::make_shared(nullptr, spatiallyNestableOverride); model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8b1ba75259..36273c1f07 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -807,7 +807,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { const FBXCluster& cluster = mesh.clusters.at(0); - auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex); + auto jointMatrix = _model->getRig().getJointTransform(cluster.jointIndex); // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); } else { @@ -1080,26 +1080,22 @@ bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index, if (!_model) { return false; } - RigPointer rig = _model->getRig(); - if (!rig) { - return false; - } - - int jointParentIndex = rig->getJointParentIndex(index); + const Rig& rig = _model->getRig(); + int jointParentIndex = rig.getJointParentIndex(index); if (jointParentIndex == -1) { return setLocalJointRotation(index, rotation); } bool success; AnimPose jointParentPose; - success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); + success = rig.getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); if (!success) { return false; } AnimPose jointParentInversePose = jointParentPose.inverse(); AnimPose jointAbsolutePose; // in rig frame - success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); + success = rig.getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); if (!success) { return false; } @@ -1113,26 +1109,23 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind if (!_model) { return false; } - RigPointer rig = _model->getRig(); - if (!rig) { - return false; - } + const Rig& rig = _model->getRig(); - int jointParentIndex = rig->getJointParentIndex(index); + int jointParentIndex = rig.getJointParentIndex(index); if (jointParentIndex == -1) { return setLocalJointTranslation(index, translation); } bool success; AnimPose jointParentPose; - success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); + success = rig.getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose); if (!success) { return false; } AnimPose jointParentInversePose = jointParentPose.inverse(); AnimPose jointAbsolutePose; // in rig frame - success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); + success = rig.getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose); if (!success) { return false; } @@ -1248,20 +1241,16 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) { } int RenderableModelEntityItem::getJointIndex(const QString& name) const { - if (_model && _model->isActive()) { - RigPointer rig = _model->getRig(); - return rig->indexOfJoint(name); - } - return -1; + return (_model && _model->isActive()) ? _model->getRig().indexOfJoint(name) : -1; } QStringList RenderableModelEntityItem::getJointNames() const { QStringList result; if (_model && _model->isActive()) { - RigPointer rig = _model->getRig(); - int jointCount = rig->getJointStateCount(); + const Rig& rig = _model->getRig(); + int jointCount = rig.getJointStateCount(); for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) { - result << rig->nameOfJoint(jointIndex); + result << rig.nameOfJoint(jointIndex); } } return result; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 66495a7054..6556f18776 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -36,15 +36,17 @@ void RenderableZoneEntityItem::changeProperties(Lambda setNewProperties) { QString oldShapeURL = getCompoundShapeURL(); glm::vec3 oldPosition = getPosition(), oldDimensions = getDimensions(); glm::quat oldRotation = getRotation(); - + setNewProperties(); - + if (oldShapeURL != getCompoundShapeURL()) { if (_model) { - delete _model; + _model.reset(); } - - _model = getModel(); + + _model = std::make_shared(); + _model->setIsWireframe(true); + _model->init(); _needsInitialSimulation = true; _model->setURL(getCompoundShapeURL()); } @@ -80,35 +82,24 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch return bytesRead; } -Model* RenderableZoneEntityItem::getModel() { - Model* model = new Model(nullptr); - model->setIsWireframe(true); - model->init(); - return model; -} - -void RenderableZoneEntityItem::initialSimulation() { - _model->setScaleToFit(true, getDimensions()); - _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); - _model->setRotation(getRotation()); - _model->setTranslation(getPosition()); - _model->simulate(0.0f); - _needsInitialSimulation = false; -} - void RenderableZoneEntityItem::updateGeometry() { if (_model && !_model->isActive() && hasCompoundShapeURL()) { // Since we have a delayload, we need to update the geometry if it has been downloaded _model->setURL(getCompoundShapeURL()); } if (_model && _model->isActive() && _needsInitialSimulation) { - initialSimulation(); + _model->setScaleToFit(true, getDimensions()); + _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + _model->simulate(0.0f); + _needsInitialSimulation = false; } } void RenderableZoneEntityItem::render(RenderArgs* args) { Q_ASSERT(getType() == EntityTypes::Zone); - + if (_drawZoneBoundaries) { switch (getShapeType()) { case SHAPE_TYPE_COMPOUND: { @@ -123,9 +114,9 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); _model->addToScene(scene, transaction); - + scene->enqueueTransaction(transaction); - + _model->setVisibleInScene(getVisible(), scene); } break; @@ -134,7 +125,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { case SHAPE_TYPE_SPHERE: { PerformanceTimer perfTimer("zone->renderPrimitive"); glm::vec4 DEFAULT_COLOR(1.0f, 1.0f, 1.0f, 1.0f); - + Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; @@ -159,7 +150,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { break; } } - + if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) && _model && !_model->needsFixupInScene()) { // If the model is in the scene but doesn't need to be, remove it. @@ -175,11 +166,11 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { return EntityItem::contains(point); } const_cast(this)->updateGeometry(); - + if (_model && _model->isActive() && EntityItem::contains(point)) { return _model->convexHullContains(point); } - + return false; } @@ -188,7 +179,7 @@ public: RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - + EntityItemPointer entity; }; @@ -196,7 +187,7 @@ namespace render { template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) { return ItemKey::Builder::opaqueShape(); } - + template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) { if (payload && payload->entity) { bool success; @@ -220,7 +211,7 @@ namespace render { bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { _myMetaItem = scene->allocateID(); - + auto renderData = std::make_shared(self); auto renderPayload = std::make_shared(renderData); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index c81afdab08..7241e34ce8 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -23,7 +23,7 @@ public: RenderableZoneEntityItem(const EntityItemID& entityItemID) : ZoneEntityItem(entityItemID), - _model(NULL), + _model(nullptr), _needsInitialSimulation(true) { } @@ -48,14 +48,12 @@ private: virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); } void notifyBoundChanged(); - Model* getModel(); - void initialSimulation(); void updateGeometry(); template void changeProperties(Lambda functor); - Model* _model; + ModelPointer _model; bool _needsInitialSimulation; render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6975d017b0..11cddf2634 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -957,6 +957,24 @@ void EntityTree::bumpTimestamp(EntityItemProperties& properties) { //fixme put c properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP); } +bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { + + // grab a URL representation of the entity script so we can check the host for this script + auto entityScriptURL = QUrl::fromUserInput(scriptProperty); + + for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + return true; + } + } + + return false; +} + int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -986,7 +1004,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c quint64 startFilter = 0, endFilter = 0; quint64 startLogging = 0, endLogging = 0; - bool suppressDisallowedScript = false; + bool suppressDisallowedClientScript = false; + bool suppressDisallowedServerScript = false; bool isPhysics = message.getType() == PacketType::EntityPhysics; _totalEditMessages++; @@ -1011,36 +1030,57 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } - if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { - bool passedWhiteList = false; + if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty()) { - // grab a URL representation of the entity script so we can check the host for this script - auto entityScriptURL = QUrl::fromUserInput(properties.getScript()); + bool wasDeletedBecauseOfClientScript = false; - for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) { - auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check the client entity script to make sure its URL is in the whitelist + if (!properties.getScript().isEmpty()) { + bool clientScriptPassedWhitelist = isScriptInWhitelist(properties.getScript()); - // check if this script URL matches the whitelist domain and, optionally, is beneath the path - if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && - entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { - passedWhiteList = true; - break; + if (!clientScriptPassedWhitelist) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] attempting to set entity script not on whitelist, edit rejected"; + } + + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + wasDeletedBecauseOfClientScript = true; + } else { + suppressDisallowedClientScript = true; + } } } - if (!passedWhiteList) { - if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() << "] attempting to set entity script not on whitelist, edit rejected"; - } - // If this was an add, we also want to tell the client that sent this edit that the entity was not added. - if (isAdd) { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - validEditPacket = passedWhiteList; - } else { - suppressDisallowedScript = true; + // check all server entity scripts to make sure their URLs are in the whitelist + if (!properties.getServerScripts().isEmpty()) { + bool serverScriptPassedWhitelist = isScriptInWhitelist(properties.getServerScripts()); + + if (!serverScriptPassedWhitelist) { + if (wantEditLogging()) { + qCDebug(entities) << "User [" << senderNode->getUUID() + << "] attempting to set server entity script not on whitelist, edit rejected"; + } + + // If this was an add, we also want to tell the client that sent this edit that the entity was not added. + if (isAdd) { + // Make sure we didn't already need to send back a delete because the client script failed + // the whitelist check + if (!wasDeletedBecauseOfClientScript) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + validEditPacket = false; + } + } else { + suppressDisallowedServerScript = true; + } } } + } if ((isAdd || properties.lifetimeChanged()) && @@ -1075,11 +1115,16 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c if (existingEntity && !isAdd) { - if (suppressDisallowedScript) { + if (suppressDisallowedClientScript) { bumpTimestamp(properties); properties.setScript(existingEntity->getScript()); } + if (suppressDisallowedServerScript) { + bumpTimestamp(properties); + properties.setServerScripts(existingEntity->getServerScripts()); + } + // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d7e069b005..c938c7e068 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -303,6 +303,8 @@ protected: void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); + bool isScriptInWhitelist(const QString& scriptURL); + QReadWriteLock _newlyCreatedHooksLock; QVector _newlyCreatedHooks; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index c614c8537f..471448d596 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -93,6 +93,7 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) : } const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; +const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; void AccountManager::logout() { // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file @@ -190,6 +191,12 @@ void AccountManager::setAuthURL(const QUrl& authURL) { requestProfile(); } + // prepare to refresh our token if it is about to expire + if (needsToRefreshToken()) { + qCDebug(networking) << "Refreshing access token since it will be expiring soon."; + refreshAccessToken(); + } + // tell listeners that the auth endpoint has changed emit authEndpointChanged(); } @@ -448,6 +455,12 @@ bool AccountManager::hasValidAccessToken() { return false; } else { + + if (!_isWaitingForTokenRefresh && needsToRefreshToken()) { + qCDebug(networking) << "Refreshing access token since it will be expiring soon."; + refreshAccessToken(); + } + return true; } } @@ -463,6 +476,15 @@ bool AccountManager::checkAndSignalForAccessToken() { return hasToken; } +bool AccountManager::needsToRefreshToken() { + if (!_accountInfo.getAccessToken().token.isEmpty()) { + qlonglong expireThreshold = QDateTime::currentDateTime().addSecs(1 * 60 * 60).toMSecsSinceEpoch(); + return _accountInfo.getAccessToken().expiryTimestamp < expireThreshold; + } else { + return false; + } +} + void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) { // replace the account info access token with a new OAuthAccessToken OAuthAccessToken newOAuthToken; @@ -495,8 +517,6 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QUrl grantURL = _authURL; grantURL.setPath("/oauth/token"); - const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; - QByteArray postData; postData.append("grant_type=password&"); postData.append("username=" + login + "&"); @@ -520,8 +540,6 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { QUrl grantURL = _authURL; grantURL.setPath("/oauth/token"); - const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; - QByteArray postData; postData.append("grant_type=password&"); postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); @@ -535,6 +553,32 @@ void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } +void AccountManager::refreshAccessToken() { + + _isWaitingForTokenRefresh = true; + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest request; + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); + + QUrl grantURL = _authURL; + grantURL.setPath("/oauth/token"); + + QByteArray postData; + postData.append("grant_type=refresh_token&"); + postData.append("refresh_token=" + QUrl::toPercentEncoding(_accountInfo.getAccessToken().refreshToken) + "&"); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + + request.setUrl(grantURL); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QNetworkReply* requestReply = networkAccessManager.post(request, postData); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::refreshAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(refreshAccessTokenError(QNetworkReply::NetworkError))); +} + void AccountManager::requestAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); @@ -573,10 +617,47 @@ void AccountManager::requestAccessTokenFinished() { void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { // TODO: error handling - qCDebug(networking) << "AccountManager requestError - " << error; + qCDebug(networking) << "AccountManager: failed to fetch access token - " << error; emit loginFailed(); } +void AccountManager::refreshAccessTokenFinished() { + QNetworkReply* requestReply = reinterpret_cast(sender()); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); + const QJsonObject& rootObject = jsonResponse.object(); + + if (!rootObject.contains("error")) { + // construct an OAuthAccessToken from the json object + + if (!rootObject.contains("access_token") || !rootObject.contains("expires_in") + || !rootObject.contains("token_type")) { + // TODO: error handling - malformed token response + qCDebug(networking) << "Received a response for refresh grant that is missing one or more expected values."; + } else { + // clear the path from the response URL so we have the right root URL for this access token + QUrl rootURL = requestReply->url(); + rootURL.setPath(""); + + qCDebug(networking) << "Storing an account with a refreshed access-token for" << qPrintable(rootURL.toString()); + + _accountInfo.setAccessTokenFromJSON(rootObject); + + persistAccountToFile(); + } + } else { + qCWarning(networking) << "Error in response for refresh grant - " << rootObject["error_description"].toString(); + } + + _isWaitingForTokenRefresh = false; +} + +void AccountManager::refreshAccessTokenError(QNetworkReply::NetworkError error) { + // TODO: error handling + qCDebug(networking) << "AccountManager: failed to refresh access token - " << error; + _isWaitingForTokenRefresh = false; +} + void AccountManager::requestProfile() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index eb4d224501..dd2216957f 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -79,6 +79,7 @@ public: bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool hasValidAccessToken(); + bool needsToRefreshToken(); Q_INVOKABLE bool checkAndSignalForAccessToken(); void setAccessTokenForCurrentAuthURL(const QString& accessToken); @@ -97,10 +98,13 @@ public: public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); + void refreshAccessToken(); void requestAccessTokenFinished(); + void refreshAccessTokenFinished(); void requestProfileFinished(); void requestAccessTokenError(QNetworkReply::NetworkError error); + void refreshAccessTokenError(QNetworkReply::NetworkError error); void requestProfileError(QNetworkReply::NetworkError error); void logout(); void generateNewUserKeypair() { generateNewKeypair(); } @@ -141,6 +145,7 @@ private: QMap _pendingCallbackMap; DataServerAccountInfo _accountInfo; + bool _isWaitingForTokenRefresh { false }; bool _isAgent { false }; bool _isWaitingForKeypairResponse { false }; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 2e3d0385cd..f3ee846d39 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -17,7 +17,7 @@ using namespace render; -CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) +CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( @@ -29,8 +29,16 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - CauterizedModel* skeleton = static_cast(_model); - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE); + if (useCauterizedMesh) { + ModelPointer model = _model.lock(); + if (model) { + CauterizedModel* skeleton = static_cast(model.get()); + useCauterizedMesh = useCauterizedMesh && skeleton->getEnableCauterization(); + } else { + useCauterizedMesh = false; + } + } if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 010cd6fcb6..5e3135ea84 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -13,7 +13,7 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: - CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 14625952ea..47ada457a0 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -16,8 +16,8 @@ #include "RenderUtilsLogging.h" -CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : - Model(rig, parent) { +CauterizedModel::CauterizedModel(QObject* parent) : + Model(parent) { } CauterizedModel::~CauterizedModel() { @@ -78,7 +78,7 @@ void CauterizedModel::createVisibleRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - auto ptr = std::make_shared(this, i, partIndex, shapeID, transform, offset); + auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); shapeID++; } @@ -107,7 +107,7 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } @@ -130,14 +130,14 @@ void CauterizedModel::updateClusterMatrices() { 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; + auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } @@ -207,11 +207,12 @@ void CauterizedModel::updateRenderItems() { QList keys = self->getRenderItems().keys(); foreach (auto itemID, keys) { transaction.updateItem(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { - if (data._model && data._model->isLoaded()) { + ModelPointer model = data._model.lock(); + if (model && model->isLoaded()) { // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == data._model->getGeometryCounter()) { + if (deleteGeometryCounter == model->getGeometryCounter()) { // this stuff identical to what happens in regular Model - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + const Model::MeshState& state = model->getMeshState(data._meshIndex); Transform renderTransform = modelTransform; if (state.clusterMatrices.size() == 1) { renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); @@ -219,7 +220,7 @@ void CauterizedModel::updateRenderItems() { data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); // this stuff for cauterized mesh - CauterizedModel* cModel = static_cast(data._model); + CauterizedModel* cModel = static_cast(model.get()); const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); renderTransform = modelTransform; if (cState.clusterMatrices.size() == 1) { diff --git a/libraries/render-utils/src/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h index dcff7bd12d..d16c928ba6 100644 --- a/libraries/render-utils/src/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -16,7 +16,7 @@ class CauterizedModel : public Model { Q_OBJECT public: - CauterizedModel(RigPointer rig, QObject* parent); + CauterizedModel(QObject* parent); virtual ~CauterizedModel(); void flagAsCauterized() { _isCauterized = true; } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2e08420073..b4fd7e7d2a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -14,7 +14,6 @@ #include #include "DeferredLightingEffect.h" -#include "Model.h" #include "EntityItem.h" using namespace render; @@ -321,13 +320,13 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } } -ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : - _model(model), +ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : _meshIndex(_meshIndex), _shapeID(shapeIndex) { - assert(_model && _model->isLoaded()); - auto& modelMesh = _model->getGeometry()->getMeshes().at(_meshIndex); + assert(model && model->isLoaded()); + _model = model; + auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); updateMeshPart(modelMesh, partIndex); updateTransform(transform, offsetTransform); @@ -335,20 +334,21 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par } void ModelMeshPartPayload::initCache() { - assert(_model->isLoaded()); + ModelPointer model = _model.lock(); + assert(model && model->isLoaded()); if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - const FBXGeometry& geometry = _model->getFBXGeometry(); + const FBXGeometry& geometry = model->getFBXGeometry(); const FBXMesh& mesh = geometry.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); } - auto networkMaterial = _model->getGeometry()->getShapeMaterial(_shapeID); + auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID); if (networkMaterial) { _drawMaterial = networkMaterial; } @@ -370,29 +370,31 @@ ItemKey ModelMeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); - if (!_model->isVisible()) { - builder.withInvisible(); - } + ModelPointer model = _model.lock(); + if (model) { + if (!model->isVisible()) { + builder.withInvisible(); + } - if (_model->isLayeredInFront()) { - builder.withLayered(); - } + if (model->isLayeredInFront()) { + builder.withLayered(); + } - if (_isBlendShaped || _isSkinned) { - builder.withDeformed(); - } + if (_isBlendShaped || _isSkinned) { + builder.withDeformed(); + } - if (_drawMaterial) { - auto matKey = _drawMaterial->getKey(); - if (matKey.isTranslucent()) { + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTranslucent()) { + builder.withTransparent(); + } + } + + if (_fadeState != FADE_COMPLETE) { builder.withTransparent(); } } - - if (_fadeState != FADE_COMPLETE) { - builder.withTransparent(); - } - return builder.build(); } @@ -400,7 +402,8 @@ int ModelMeshPartPayload::getLayer() const { // MAgic number while we are defining the layering mechanism: const int LAYER_3D_FRONT = 1; const int LAYER_3D = 0; - if (_model->isLayeredInFront()) { + ModelPointer model = _model.lock(); + if (model && model->isLayeredInFront()) { return LAYER_3D_FRONT; } else { return LAYER_3D; @@ -410,15 +413,16 @@ int ModelMeshPartPayload::getLayer() const { ShapeKey ModelMeshPartPayload::getShapeKey() const { // guard against partially loaded meshes - if (!_model || !_model->isLoaded() || !_model->getGeometry()) { + ModelPointer model = _model.lock(); + if (!model || !model->isLoaded() || !model->getGeometry()) { return ShapeKey::Builder::invalid(); } - const FBXGeometry& geometry = _model->getFBXGeometry(); - const auto& networkMeshes = _model->getGeometry()->getMeshes(); + const FBXGeometry& geometry = model->getFBXGeometry(); + const auto& networkMeshes = model->getGeometry()->getMeshes(); // guard against partially loaded meshes - if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size()) { + if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)model->_meshStates.size()) { return ShapeKey::Builder::invalid(); } @@ -427,8 +431,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { // 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 >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { - _model->_needsFixupInScene = true; // trigger remove/add cycle - _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + model->_needsFixupInScene = true; // trigger remove/add cycle + model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return ShapeKey::Builder::invalid(); } @@ -452,7 +456,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { bool isUnlit = drawMaterialKey.isUnlit(); bool isSkinned = _isSkinned; - bool wireframe = _model->isWireframe(); + bool wireframe = model->isWireframe(); if (wireframe) { isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false; @@ -488,18 +492,22 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { if (!_isBlendShaped) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); - batch.setInputStream(0, _drawMesh->getVertexStream()); } else { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); - batch.setInputFormat((_drawMesh->getVertexFormat())); - batch.setInputBuffer(0, _model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); + ModelPointer model = _model.lock(); + if (model) { + batch.setInputBuffer(0, model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); + } else { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + batch.setInputFormat((_drawMesh->getVertexFormat())); + batch.setInputStream(0, _drawMesh->getVertexStream()); + } } if (_fadeState != FADE_COMPLETE) { @@ -530,7 +538,10 @@ float ModelMeshPartPayload::computeFadeAlpha() { if (fadeAlpha >= 1.0f) { _fadeState = FADE_COMPLETE; // when fade-in completes we flag model for one last "render item update" - _model->setRenderItemsNeedUpdate(); + ModelPointer model = _model.lock(); + if (model) { + model->setRenderItemsNeedUpdate(); + } return 1.0f; } return Interpolate::simpleNonLinearBlend(fadeAlpha); @@ -539,26 +550,27 @@ float ModelMeshPartPayload::computeFadeAlpha() { void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!_model->addedToScene() || !_model->isVisible()) { + ModelPointer model = _model.lock(); + if (!model || !model->addedToScene() || !model->isVisible()) { return; // bail asap } if (_fadeState == FADE_WAITING_TO_START) { - if (_model->isLoaded()) { + if (model->isLoaded()) { if (EntityItem::getEntitiesShouldFadeFunction()()) { _fadeStartTime = usecTimestampNow(); _fadeState = FADE_IN_PROGRESS; } else { _fadeState = FADE_COMPLETE; } - _model->setRenderItemsNeedUpdate(); + model->setRenderItemsNeedUpdate(); } else { return; } } - if (_materialNeedsUpdate && _model->getGeometry()->areTexturesLoaded()) { - _model->setRenderItemsNeedUpdate(); + if (_materialNeedsUpdate && model->getGeometry()->areTexturesLoaded()) { + model->setRenderItemsNeedUpdate(); _materialNeedsUpdate = false; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 11d1bbf6a7..5d12e60ce3 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -21,6 +21,8 @@ #include +#include "Model.h" + const uint8_t FADE_WAITING_TO_START = 0; const uint8_t FADE_IN_PROGRESS = 1; const uint8_t FADE_COMPLETE = 2; @@ -83,7 +85,7 @@ namespace render { class ModelMeshPartPayload : public MeshPartPayload { public: - ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); + ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; @@ -110,7 +112,7 @@ public: void computeAdjustedLocalBound(const QVector& clusterMatrices); gpu::BufferPointer _clusterBuffer; - Model* _model; + ModelWeakPointer _model; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e09afa3f31..a2ee2c63b3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -78,7 +78,7 @@ void initCollisionMaterials() { } } -Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : +Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : QObject(parent), _renderGeometry(), _collisionGeometry(), @@ -96,8 +96,7 @@ Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestab _isVisible(true), _blendNumber(0), _appliedBlendNumber(0), - _isWireframe(false), - _rig(rig) + _isWireframe(false) { // we may have been created in the network thread, but we live in the main thread if (_viewState) { @@ -236,13 +235,14 @@ void Model::updateRenderItems() { render::Transaction transaction; foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { transaction.updateItem(itemID, [deleteGeometryCounter](ModelMeshPartPayload& data) { - if (data._model && data._model->isLoaded()) { + ModelPointer model = data._model.lock(); + if (model && model->isLoaded()) { // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { - Transform modelTransform = data._model->getTransform(); + if (deleteGeometryCounter == model->_deleteGeometryCounter) { + Transform modelTransform = model->getTransform(); modelTransform.setScale(glm::vec3(1.0f)); - const Model::MeshState& state = data._model->getMeshState(data._meshIndex); + const Model::MeshState& state = model->getMeshState(data._meshIndex); Transform renderTransform = modelTransform; if (state.clusterMatrices.size() == 1) { renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); @@ -271,7 +271,7 @@ void Model::updateRenderItems() { void Model::initJointTransforms() { if (isLoaded()) { glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig->setModelOffset(modelOffset); + _rig.setModelOffset(modelOffset); } } @@ -281,7 +281,7 @@ void Model::init() { void Model::reset() { if (isLoaded()) { const FBXGeometry& geometry = getFBXGeometry(); - _rig->reset(geometry); + _rig.reset(geometry); } } @@ -294,7 +294,8 @@ bool Model::updateGeometry() { _needsReload = false; - if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + // TODO: should all Models have a valid _rig? + if (_rig.jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { initJointStates(); assert(_meshStates.empty()); @@ -326,7 +327,7 @@ void Model::initJointStates() { const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig->initJointStates(geometry, modelOffset); + _rig.initJointStates(geometry, modelOffset); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -746,19 +747,19 @@ Extents Model::getUnscaledMeshExtents() const { } void Model::clearJointState(int index) { - _rig->clearJointState(index); + _rig.clearJointState(index); } void Model::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - _rig->setJointState(index, valid, rotation, translation, priority); + _rig.setJointState(index, valid, rotation, translation, priority); } void Model::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { - _rig->setJointRotation(index, valid, rotation, priority); + _rig.setJointRotation(index, valid, rotation, priority); } void Model::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - _rig->setJointTranslation(index, valid, translation, priority); + _rig.setJointTranslation(index, valid, translation, priority); } int Model::getParentJointIndex(int jointIndex) const { @@ -810,8 +811,10 @@ void Model::setURL(const QUrl& url) { deleteGeometry(); auto resource = DependencyManager::get()->getGeometryResource(url); - resource->setLoadPriority(this, _loadingPriority); - _renderWatcher.setResource(resource); + if (resource) { + resource->setLoadPriority(this, _loadingPriority); + _renderWatcher.setResource(resource); + } onInvalidate(); } @@ -823,43 +826,43 @@ void Model::loadURLFinished(bool success) { } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { - return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); + return _rig.getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation); } bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { - return _rig->getJointPosition(jointIndex, position); + return _rig.getJointPosition(jointIndex, position); } bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { - return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation); + return _rig.getJointRotationInWorldFrame(jointIndex, rotation, _rotation); } bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { - return _rig->getJointRotation(jointIndex, rotation); + return _rig.getJointRotation(jointIndex, rotation); } bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const { - return _rig->getJointTranslation(jointIndex, translation); + return _rig.getJointTranslation(jointIndex, translation); } bool Model::getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotationOut) const { - return _rig->getAbsoluteJointRotationInRigFrame(jointIndex, rotationOut); + return _rig.getAbsoluteJointRotationInRigFrame(jointIndex, rotationOut); } bool Model::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translationOut) const { - return _rig->getAbsoluteJointTranslationInRigFrame(jointIndex, translationOut); + return _rig.getAbsoluteJointTranslationInRigFrame(jointIndex, translationOut); } bool Model::getRelativeDefaultJointRotation(int jointIndex, glm::quat& rotationOut) const { - return _rig->getRelativeDefaultJointRotation(jointIndex, rotationOut); + return _rig.getRelativeDefaultJointRotation(jointIndex, rotationOut); } bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& translationOut) const { - return _rig->getRelativeDefaultJointTranslation(jointIndex, translationOut); + return _rig.getRelativeDefaultJointTranslation(jointIndex, translationOut); } bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { - return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); + return _rig.getJointCombinedRotation(jointIndex, rotation, _rotation); } QStringList Model::getJointNames() const { @@ -1047,7 +1050,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { _needsUpdateClusterMatrices = true; glm::mat4 rigToWorldTransform = createMatFromQuatAndPos(getRotation(), getTranslation()); - _rig->updateAnimations(deltaTime, parentTransform, rigToWorldTransform); + _rig.updateAnimations(deltaTime, parentTransform, rigToWorldTransform); } void Model::computeMeshPartLocalBounds() { @@ -1071,7 +1074,7 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); + auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } @@ -1098,19 +1101,19 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm: const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); - _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); + _rig.inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); + return _rig.restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); + return _rig.getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } bool Model::maybeStartBlender() { @@ -1153,7 +1156,7 @@ void Model::deleteGeometry() { _deleteGeometryCounter++; _blendedVertexBuffers.clear(); _meshStates.clear(); - _rig->destroyAnimGraph(); + _rig.destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); _renderGeometry.reset(); _collisionGeometry.reset(); @@ -1223,7 +1226,7 @@ void Model::createVisibleRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - _modelMeshRenderItems << std::make_shared(this, i, partIndex, shapeID, transform, offset); + _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); shapeID++; } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5899ccf6b5..9daffcc00b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -68,7 +68,7 @@ public: static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; } - Model(RigPointer rig, QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr); + Model(QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr); virtual ~Model(); inline ModelPointer getThisPointer() const { @@ -174,7 +174,7 @@ public: } /// Returns the number of joint states in the model. - int getJointStateCount() const { return (int)_rig->getJointStateCount(); } + int getJointStateCount() const { return (int)_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; @@ -223,7 +223,8 @@ public: return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); } - virtual RigPointer getRig() const { return _rig; } + Rig& getRig() { return _rig; } + const Rig& getRig() const { return _rig; } const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } @@ -390,7 +391,7 @@ protected: mutable bool _needsUpdateTextures { true }; friend class ModelMeshPartPayload; - RigPointer _rig; + Rig _rig; uint32_t _deleteGeometryCounter { 0 }; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 8fef0f8f77..f15b696006 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -8,11 +8,9 @@ #include "SoftAttachmentModel.h" -SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : - CauterizedModel(rig, parent), +SoftAttachmentModel::SoftAttachmentModel(QObject* parent, const Rig& rigOverride) : + CauterizedModel(parent), _rigOverride(rigOverride) { - assert(_rig); - assert(_rigOverride); } SoftAttachmentModel::~SoftAttachmentModel() { @@ -24,11 +22,11 @@ void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) } int SoftAttachmentModel::getJointIndexOverride(int i) const { - QString name = _rig->nameOfJoint(i); + QString name = _rig.nameOfJoint(i); if (name.isEmpty()) { return -1; } - return _rigOverride->indexOfJoint(name); + return _rigOverride.indexOfJoint(name); } // virtual @@ -51,10 +49,10 @@ void SoftAttachmentModel::updateClusterMatrices() { // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) { - jointMatrix = _rigOverride->getJointTransform(jointIndexOverride); + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); } else { - jointMatrix = _rig->getJointTransform(cluster.jointIndex); + jointMatrix = _rig.getJointTransform(cluster.jointIndex); } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } diff --git a/libraries/render-utils/src/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h index b66c1a289a..4335c1634e 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.h +++ b/libraries/render-utils/src/SoftAttachmentModel.h @@ -23,7 +23,7 @@ class SoftAttachmentModel : public CauterizedModel { Q_OBJECT public: - SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride); + SoftAttachmentModel(QObject* parent, const Rig& rigOverride); ~SoftAttachmentModel(); void updateRig(float deltaTime, glm::mat4 parentTransform) override; @@ -32,7 +32,7 @@ public: protected: int getJointIndexOverride(int i) const; - RigPointer _rigOverride; + const Rig& _rigOverride; }; #endif // hifi_SoftAttachmentModel_h diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index e8d4c41dff..d5de9226c0 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -47,7 +47,7 @@ #include #include "RigTests.h" -static void reportJoint(RigPointer rig, int index) { // Handy for debugging +static void reportJoint(const Rig& rig, int index) { // Handy for debugging std::cout << "\n"; std::cout << index << " " << rig->getAnimSkeleton()->getJointName(index).toUtf8().data() << "\n"; glm::vec3 pos; @@ -58,16 +58,16 @@ static void reportJoint(RigPointer rig, int index) { // Handy for debugging std::cout << " rot:" << safeEulerAngles(rot) << "\n"; std::cout << "\n"; } -static void reportByName(RigPointer rig, const QString& name) { +static void reportByName(const Rig& rig, const QString& name) { int jointIndex = rig->indexOfJoint(name); reportJoint(rig, jointIndex); } -static void reportAll(RigPointer rig) { +static void reportAll(const Rig& rig) { for (int i = 0; i < rig->getJointStateCount(); i++) { reportJoint(rig, i); } } -static void reportSome(RigPointer rig) { +static void reportSome(const Rig& 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); @@ -91,8 +91,7 @@ void RigTests::initTestCase() { #endif QVERIFY((bool)geometry); - _rig = std::make_shared(); - _rig->initJointStates(*geometry, glm::mat4()); + _rig.initJointStates(*geometry, glm::mat4()); std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl; reportAll(_rig); } diff --git a/tests/animation/src/RigTests.h b/tests/animation/src/RigTests.h index 4280c0a8fa..3242c27b99 100644 --- a/tests/animation/src/RigTests.h +++ b/tests/animation/src/RigTests.h @@ -43,13 +43,13 @@ class RigTests : public QObject { Q_OBJECT - + private slots: void initTestCase(); void initialPoseArmsDown(); private: - RigPointer _rig; + Rig _rig; }; #endif // hifi_RigTests_h