diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index fda226b934..157154606f 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -153,11 +153,11 @@ void Agent::run() { qDebug() << "Downloaded script:" << scriptContents; - _scriptEngine = new ScriptEngine(scriptContents, _payload); + _scriptEngine = std::unique_ptr(new ScriptEngine(scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do // setup an Avatar for the script to use - ScriptableAvatar scriptedAvatar(_scriptEngine); + ScriptableAvatar scriptedAvatar(_scriptEngine.get()); scriptedAvatar.setForceFaceTrackerConnected(true); // call model URL setters with empty URLs so our avatar, if user, will have the default models @@ -191,22 +191,21 @@ void Agent::run() { auto entityScriptingInterface = DependencyManager::get(); _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); + + // we need to make sure that init has been called for our EntityScriptingInterface + // so that it actually has a jurisdiction listener when we ask it for it next + entityScriptingInterface->init(); _entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener()); + _entityViewer.init(); + entityScriptingInterface->setEntityTree(_entityViewer.getTree()); // wire up our additional agent related processing to the update signal - QObject::connect(_scriptEngine, &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); + QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); _scriptEngine->run(); setFinished(true); - - // kill the avatar identity timer - delete _avatarIdentityTimer; - - // delete the script engine - delete _scriptEngine; - } void Agent::setIsAvatar(bool isAvatar) { @@ -227,10 +226,17 @@ void Agent::setIsAvatar(bool isAvatar) { } if (!_isAvatar) { - delete _avatarIdentityTimer; - _avatarIdentityTimer = NULL; - delete _avatarBillboardTimer; - _avatarBillboardTimer = NULL; + if (_avatarIdentityTimer) { + _avatarIdentityTimer->stop(); + delete _avatarIdentityTimer; + _avatarIdentityTimer = nullptr; + } + + if (_avatarBillboardTimer) { + _avatarIdentityTimer->stop(); + delete _avatarIdentityTimer; + _avatarBillboardTimer = nullptr; + } } } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 33a8eb58c2..ab000015d5 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -12,6 +12,7 @@ #ifndef hifi_Agent_h #define hifi_Agent_h +#include #include #include @@ -61,7 +62,7 @@ private slots: void processAgentAvatarAndAudio(float deltaTime); private: - ScriptEngine* _scriptEngine; + std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; QTimer* _pingTimer; diff --git a/examples/entityScripts/sprayPaintCan.js b/examples/entityScripts/sprayPaintCan.js index 4407140184..aa04e94341 100644 --- a/examples/entityScripts/sprayPaintCan.js +++ b/examples/entityScripts/sprayPaintCan.js @@ -1,7 +1,8 @@ (function() { // Script.include("../libraries/utils.js"); //Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge - Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js"); + + Script.include("../libraries/utils.js"); GRAB_FRAME_USER_DATA_KEY = "grabFrame"; this.userData = {}; @@ -56,26 +57,21 @@ timeSinceLastMoved = 0; } - if (self.userData.grabKey && self.userData.grabKey.activated === true) { + //Only activate for the user who grabbed the object + if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId == MyAvatar.sessionUUID) { if (self.activated !== true) { //We were just grabbed, so create a particle system self.grab(); - Entities.editEntity(self.paintStream, { - animationSettings: startSetting - }); } //Move emitter to where entity is always when its activated self.sprayStream(); } else if (self.userData.grabKey && self.userData.grabKey.activated === false && self.activated) { - Entities.editEntity(self.paintStream, { - animationSettings: stopSetting - }); - self.activated = false; + self.letGo(); } } this.grab = function() { - self.activated = true; + this.activated = true; var animationSettings = JSON.stringify({ fps: 30, loop: true, @@ -92,9 +88,9 @@ emitVelocity: ZERO_VEC, emitAcceleration: ZERO_VEC, velocitySpread: { - x: .02, - y: .02, - z: 0.02 + x: .1, + y: .1, + z: 0.1 }, emitRate: 100, particleRadius: 0.01, @@ -103,14 +99,14 @@ green: 20, blue: 150 }, - lifetime: 500, //probably wont be holding longer than this straight + lifetime: 50, //probably wont be holding longer than this straight }); - } this.letGo = function() { - self.activated = false; + this.activated = false; Entities.deleteEntity(this.paintStream); + this.paintStream = null; } this.reset = function() { @@ -123,8 +119,7 @@ } this.sprayStream = function() { - var forwardVec = Quat.getFront(self.properties.rotation); - forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec); + var forwardVec = Quat.getFront(Quat.multiply(self.properties.rotation , Quat.fromPitchYawRollDegrees(0, 90, 0))); forwardVec = Vec3.normalize(forwardVec); var upVec = Quat.getUp(self.properties.rotation); @@ -132,11 +127,10 @@ position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y)) Entities.editEntity(self.paintStream, { position: position, - emitVelocity: Vec3.multiply(forwardVec, 4) + emitVelocity: Vec3.multiply(5, forwardVec) }); //Now check for an intersection with an entity - //move forward so ray doesnt intersect with gun var origin = Vec3.sum(position, forwardVec); var pickRay = { @@ -216,6 +210,8 @@ this.entityId = entityId; this.properties = Entities.getEntityProperties(self.entityId); this.getUserData(); + + //Only activate for the avatar who is grabbing the can! if (this.userData.grabKey && this.userData.grabKey.activated) { this.activated = true; } @@ -235,7 +231,9 @@ this.unload = function() { Script.update.disconnect(this.update); - Entities.deleteEntity(this.paintStream); + if(this.paintStream) { + Entities.deleteEntity(this.paintStream); + } this.strokes.forEach(function(stroke) { Entities.deleteEntity(stroke); }); @@ -244,6 +242,7 @@ }); + function randFloat(min, max) { return Math.random() * (max - min) + min; } diff --git a/examples/sprayPaintSpawner.js b/examples/sprayPaintSpawner.js index 77b74e6520..3b9cee6ef4 100644 --- a/examples/sprayPaintSpawner.js +++ b/examples/sprayPaintSpawner.js @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html //Just temporarily using my own bucket here so others can test the entity. Once PR is tested and merged, then the entity script will appear in its proper place in S3, and I wil switch it -var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v1"; +// var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v6 "; +var scriptURL = Script.resolvePath("entityScripts/sprayPaintCan.js?v2"); var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); var sprayCan = Entities.addEntity({ type: "Model", @@ -32,7 +32,10 @@ var sprayCan = Entities.addEntity({ }); function cleanup() { - Entities.deleteEntity(sprayCan); + + // Uncomment the below line to delete sprayCan on script reload- for faster iteration during development + // Entities.deleteEntity(sprayCan); } Script.scriptEnding.connect(cleanup); + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b6e0a871b3..a313308023 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -182,9 +182,6 @@ public: using namespace std; -// Starfield information -const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB - static QTimer* locationUpdateTimer = NULL; static QTimer* balanceUpdateTimer = NULL; static QTimer* identityPacketTimer = NULL; @@ -370,6 +367,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _lastFaceTrackerUpdate(0), _applicationOverlay() { + thread()->setObjectName("Main Thread"); + setInstance(this); _entityClipboard->createRootElement(); @@ -460,13 +459,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : audioThread->start(); - QThread* assetThread = new QThread; - - assetThread->setObjectName("Asset Thread"); + // Setup AssetClient auto assetClient = DependencyManager::get(); - + QThread* assetThread = new QThread; + assetThread->setObjectName("Asset Thread"); assetClient->moveToThread(assetThread); - + connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); assetThread->start(); const DomainHandler& domainHandler = nodeList->getDomainHandler(); @@ -855,8 +853,7 @@ void Application::cleanupBeforeQuit() { } void Application::emptyLocalCache() { - QNetworkDiskCache* cache = qobject_cast(NetworkAccessManager::getInstance().cache()); - if (cache) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { qDebug() << "DiskCacheEditor::clear(): Clearing disk cache."; cache->clear(); } @@ -4350,6 +4347,8 @@ void Application::stopScript(const QString &scriptName, bool restart) { } void Application::reloadAllScripts() { + DependencyManager::get()->clearCache(); + getEntities()->reloadEntityScripts(); stopAllScripts(true); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e165b5c617..69f7516430 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1302,7 +1302,7 @@ void MyAvatar::initAnimGraph() { // or run a local web-server // python -m SimpleHTTPServer& //auto graphUrl = QUrl("http://localhost:8000/avatar.json"); - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/5f2a4e268d35147c83d44881e268f83a6296e89b/ik-avatar-hands.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/72517b231f606b724c5169e02642e401f9af5a54/ik-avatar-hands.json"); _rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp index 2f2b924e13..a33f28e240 100644 --- a/interface/src/ui/DiskCacheEditor.cpp +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -140,8 +140,7 @@ void DiskCacheEditor::clear() { "You are about to erase all the content of the disk cache," "are you sure you want to do that?"); if (buttonClicked == QMessageBox::Yes) { - QNetworkDiskCache* cache = qobject_cast(NetworkAccessManager::getInstance().cache()); - if (cache) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { qDebug() << "DiskCacheEditor::clear(): Clearing disk cache."; cache->clear(); } diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 63c66a2b9d..bc95565f6f 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -13,7 +13,7 @@ #include "AnimationLogging.h" #include "AnimUtil.h" -AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha) { diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 3a09245575..56acd5c2f7 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -27,12 +27,12 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const std::string& id, float alpha); + AnimBlendLinear(const QString& id, float alpha); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; - void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } protected: // for AnimDebugDraw rendering @@ -42,7 +42,7 @@ protected: float _alpha; - std::string _alphaVar; + QString _alphaVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index fdc5fc504a..23aa884933 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,7 +13,7 @@ #include "AnimationLogging.h" #include "AnimUtil.h" -AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : +AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), @@ -68,9 +68,9 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, return _poses; } -void AnimClip::loadURL(const std::string& url) { +void AnimClip::loadURL(const QString& url) { auto animCache = DependencyManager::get(); - _networkAnim = animCache->getAnimation(QString::fromStdString(url)); + _networkAnim = animCache->getAnimation(url); _url = url; } @@ -127,7 +127,7 @@ void AnimClip::copyFromNetworkAnim() { for (int i = 0; i < animJointCount; i++) { int skeletonJoint = _skeleton->nameToJointIndex(animJoints.at(i).name); if (skeletonJoint == -1) { - qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url.c_str(); + qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url; } jointMap.push_back(skeletonJoint); } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 1b9649cc3e..3a76870c98 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,19 +25,19 @@ class AnimClip : public AnimNode { public: friend class AnimTests; - AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag); + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; - void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; } - void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; } - void setTimeScaleVar(const std::string& timeScaleVar) { _timeScaleVar = timeScaleVar; } - void setLoopFlagVar(const std::string& loopFlagVar) { _loopFlagVar = loopFlagVar; } - void setFrameVar(const std::string& frameVar) { _frameVar = frameVar; } + void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; } + void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; } + void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } + void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; } + void setFrameVar(const QString& frameVar) { _frameVar = frameVar; } protected: - void loadURL(const std::string& url); + void loadURL(const QString& url); virtual void setCurrentFrameInternal(float frame) override; @@ -53,18 +53,18 @@ protected: // _anim[frame][joint] std::vector _anim; - std::string _url; + QString _url; float _startFrame; float _endFrame; float _timeScale; bool _loopFlag; float _frame; - std::string _startFrameVar; - std::string _endFrameVar; - std::string _timeScaleVar; - std::string _loopFlagVar; - std::string _frameVar; + QString _startFrameVar; + QString _endFrameVar; + QString _timeScaleVar; + QString _loopFlagVar; + QString _frameVar; // no copies AnimClip(const AnimClip&) = delete; diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 49ff7feda6..36b23c313e 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -9,6 +9,7 @@ #include "AnimInverseKinematics.h" +#include #include #include @@ -16,7 +17,7 @@ #include "SwingTwistConstraint.h" #include "AnimationLogging.h" -AnimInverseKinematics::AnimInverseKinematics(const std::string& id) : AnimNode(AnimNode::Type::InverseKinematics, id) { +AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimNode::Type::InverseKinematics, id) { } AnimInverseKinematics::~AnimInverseKinematics() { @@ -54,7 +55,20 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) { // if there are dups, last one wins. - _targetVarVec.push_back(IKTargetVar(jointName, positionVar.toStdString(), rotationVar.toStdString())); + bool found = false; + for (auto& targetVar: _targetVarVec) { + if (targetVar.jointName == jointName) { + // update existing targetVar + targetVar.positionVar = positionVar; + targetVar.rotationVar = rotationVar; + found = true; + break; + } + } + if (!found) { + // create a new entry + _targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar)); + } } static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) { @@ -68,6 +82,12 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde return rootIndex; } +struct IKTarget { + AnimPose pose; + int index; + int rootIndex; +}; + //virtual const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) { @@ -76,43 +96,53 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar return _relativePoses; } - // evaluate target vars + // build a list of targets from _targetVarVec + std::vector targets; + bool removeUnfoundJoints = false; for (auto& targetVar : _targetVarVec) { - - // lazy look up of jointIndices and insertion into _absoluteTargets map - if (!targetVar.hasPerformedJointLookup) { - targetVar.jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); - if (targetVar.jointIndex >= 0) { - // insert into _absoluteTargets map - IKTarget target; - target.pose = AnimPose::identity; - target.rootIndex = findRootJointInSkeleton(_skeleton, targetVar.jointIndex); - _absoluteTargets[targetVar.jointIndex] = target; - - if (targetVar.jointIndex > _maxTargetIndex) { - _maxTargetIndex = targetVar.jointIndex; - } + if (targetVar.jointIndex == -1) { + // this targetVar hasn't been validated yet... + int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName); + if (jointIndex >= 0) { + // this targetVar has a valid joint --> cache the indices + targetVar.jointIndex = jointIndex; + targetVar.rootIndex = findRootJointInSkeleton(_skeleton, jointIndex); } else { qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton"; + removeUnfoundJoints = true; } - targetVar.hasPerformedJointLookup = true; - } - - if (targetVar.jointIndex >= 0) { - // update pose in _absoluteTargets map - auto iter = _absoluteTargets.find(targetVar.jointIndex); - if (iter != _absoluteTargets.end()) { + } else { + // TODO: get this done without a double-lookup of each var in animVars + if (animVars.hasKey(targetVar.positionVar) || animVars.hasKey(targetVar.rotationVar)) { + IKTarget target; AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses); - iter->second.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); - iter->second.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); + target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + target.rootIndex = targetVar.rootIndex; + target.index = targetVar.jointIndex; + targets.push_back(target); } } } - // RELAX! Don't do it. - // relaxTowardDefaults(dt); + if (removeUnfoundJoints) { + int numVars = _targetVarVec.size(); + int i = 0; + while (i < numVars) { + if (_targetVarVec[i].jointIndex == -1) { + if (numVars > 1) { + // swap i for last element + _targetVarVec[i] = _targetVarVec[numVars - 1]; + } + _targetVarVec.pop_back(); + --numVars; + } else { + ++i; + } + } + } - if (_absoluteTargets.empty()) { + if (targets.empty()) { // no IK targets but still need to enforce constraints std::map::iterator constraintItr = _constraints.begin(); while (constraintItr != _constraints.end()) { @@ -135,11 +165,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar quint64 expiry = usecTimestampNow() + MAX_IK_TIME; do { largestError = 0.0f; - for (auto& targetPair: _absoluteTargets) { + for (auto& target: targets) { int lowestMovedIndex = _relativePoses.size() - 1; - int tipIndex = targetPair.first; - AnimPose targetPose = targetPair.second.pose; - int rootIndex = targetPair.second.rootIndex; + int tipIndex = target.index; + AnimPose targetPose = target.pose; + int rootIndex = target.rootIndex; if (rootIndex != -1) { // transform targetPose into skeleton's absolute frame AnimPose& rootPose = _relativePoses[rootIndex]; @@ -150,11 +180,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar glm::vec3 tip = absolutePoses[tipIndex].trans; float error = glm::length(targetPose.trans - tip); - // descend toward root, rotating each joint to get tip closer to target - int index = _skeleton->getParentIndex(tipIndex); - while (index != -1 && error > ACCEPTABLE_RELATIVE_ERROR) { + // descend toward root, pivoting each joint to get tip closer to target + int pivotIndex = _skeleton->getParentIndex(tipIndex); + while (pivotIndex != -1 && error > ACCEPTABLE_RELATIVE_ERROR) { // compute the two lines that should be aligned - glm::vec3 jointPosition = absolutePoses[index].trans; + glm::vec3 jointPosition = absolutePoses[pivotIndex].trans; glm::vec3 leverArm = tip - jointPosition; glm::vec3 targetLine = targetPose.trans - jointPosition; @@ -173,29 +203,34 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar angle = 0.5f * angle; glm::quat deltaRotation = glm::angleAxis(angle, axis); - int parentIndex = _skeleton->getParentIndex(index); + int parentIndex = _skeleton->getParentIndex(pivotIndex); if (parentIndex == -1) { // TODO? apply constraints to root? // TODO? harvest the root's transform as movement of entire skeleton? } else { // compute joint's new parent-relative rotation // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q - glm::quat newRot = glm::normalize(glm::inverse(absolutePoses[parentIndex].rot) * deltaRotation * absolutePoses[index].rot); - RotationConstraint* constraint = getConstraint(index); + glm::quat newRot = glm::normalize(glm::inverse( + absolutePoses[parentIndex].rot) * + deltaRotation * + absolutePoses[pivotIndex].rot); + RotationConstraint* constraint = getConstraint(pivotIndex); if (constraint) { bool constrained = constraint->apply(newRot); if (constrained) { // the constraint will modify the movement of the tip so we have to compute the modified // model-frame deltaRotation // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ - deltaRotation = absolutePoses[parentIndex].rot * newRot * glm::inverse(absolutePoses[index].rot); + deltaRotation = absolutePoses[parentIndex].rot * + newRot * + glm::inverse(absolutePoses[pivotIndex].rot); } } - _relativePoses[index].rot = newRot; + _relativePoses[pivotIndex].rot = newRot; } // this joint has been changed so we check to see if it has the lowest index - if (index < lowestMovedIndex) { - lowestMovedIndex = index; + if (pivotIndex < lowestMovedIndex) { + lowestMovedIndex = pivotIndex; } // keep track of tip's new position as we descend towards root @@ -203,7 +238,7 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar error = glm::length(targetPose.trans - tip); } } - index = _skeleton->getParentIndex(index); + pivotIndex = _skeleton->getParentIndex(pivotIndex); } if (largestError < error) { largestError = error; @@ -248,12 +283,16 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars loadPoses(underPoses); } else { // relax toward underpose - const float RELAXATION_TIMESCALE = 0.125f; - const float alpha = glm::clamp(dt / RELAXATION_TIMESCALE, 0.0f, 1.0f); + // HACK: this relaxation needs to be constant per-frame rather than per-realtime + // in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts + // of this relaxation will be FPS dependent (low FPS will make the limbs align slower + // in real-time), however most people will not notice this and this problem is less + // annoying than the flutter. + const float blend = (1.0f / 60.0f) / (0.25f); // effectively: dt / RELAXATION_TIMESCALE int numJoints = (int)_relativePoses.size(); for (int i = 0; i < numJoints; ++i) { float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot)); - _relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, alpha)); + _relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend)); } } return evaluate(animVars, dt, triggersOut); @@ -277,10 +316,6 @@ void AnimInverseKinematics::clearConstraints() { _constraints.clear(); } -const glm::vec3 xAxis(1.0f, 0.0f, 0.0f); -const glm::vec3 yAxis(0.0f, 1.0f, 0.0f); -const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - void AnimInverseKinematics::initConstraints() { if (!_skeleton) { return; @@ -288,7 +323,7 @@ void AnimInverseKinematics::initConstraints() { // We create constraints for the joints shown here // (and their Left counterparts if applicable). // - // + // // O RightHand // Head / // O / @@ -306,7 +341,7 @@ void AnimInverseKinematics::initConstraints() { // y | // | | // | O---O---O RightUpLeg - // z | | | + // z | | Hips2 | // \ | | | // \| | | // x -----+ O O RightLeg @@ -334,7 +369,7 @@ void AnimInverseKinematics::initConstraints() { _constraints.clear(); for (int i = 0; i < numJoints; ++i) { - // compute the joint's baseName and remember if it was Left or not + // compute the joint's baseName and remember whether its prefix was "Left" or not QString baseName = _skeleton->getJointName(i); bool isLeft = baseName.startsWith("Left", Qt::CaseInsensitive); float mirror = isLeft ? -1.0f : 1.0f; @@ -442,7 +477,7 @@ void AnimInverseKinematics::initConstraints() { const float MAX_HAND_SWING = PI / 2.0f; minDots.push_back(cosf(MAX_HAND_SWING)); stConstraint->setSwingLimits(minDots); - + constraint = static_cast(stConstraint); } else if (baseName.startsWith("Shoulder", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); @@ -467,6 +502,18 @@ void AnimInverseKinematics::initConstraints() { minDots.push_back(cosf(MAX_SPINE_SWING)); stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); + } else if (baseName.startsWith("Hips2", Qt::CaseInsensitive)) { + SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); + stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); + const float MAX_SPINE_TWIST = PI / 8.0f; + stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST); + + std::vector minDots; + const float MAX_SPINE_SWING = PI / 14.0f; + minDots.push_back(cosf(MAX_SPINE_SWING)); + stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); @@ -488,18 +535,18 @@ void AnimInverseKinematics::initConstraints() { // we determine the max/min angles by rotating the swing limit lines from parent- to child-frame // then measure the angles to swing the yAxis into alignment - glm::vec3 hingeAxis = - mirror * zAxis; + glm::vec3 hingeAxis = - mirror * Vectors::UNIT_Z; const float MIN_ELBOW_ANGLE = 0.05f; const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f; glm::quat invReferenceRotation = glm::inverse(referenceRotation); - glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * yAxis; - glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * yAxis; + glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y; + glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y; // for the rest of the math we rotate hingeAxis into the child frame hingeAxis = referenceRotation * hingeAxis; eConstraint->setHingeAxis(hingeAxis); - glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis); + glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis); float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis)); if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) { minAngle = - minAngle; @@ -516,21 +563,21 @@ void AnimInverseKinematics::initConstraints() { ElbowConstraint* eConstraint = new ElbowConstraint(); glm::quat referenceRotation = _defaultRelativePoses[i].rot; eConstraint->setReferenceRotation(referenceRotation); - glm::vec3 hingeAxis = -1.0f * xAxis; + glm::vec3 hingeAxis = -1.0f * Vectors::UNIT_X; // we determine the max/min angles by rotating the swing limit lines from parent- to child-frame // then measure the angles to swing the yAxis into alignment const float MIN_KNEE_ANGLE = 0.0f; const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f; glm::quat invReferenceRotation = glm::inverse(referenceRotation); - glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * yAxis; - glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * yAxis; + glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y; + glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y; // for the rest of the math we rotate hingeAxis into the child frame hingeAxis = referenceRotation * hingeAxis; eConstraint->setHingeAxis(hingeAxis); - glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis); + glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis); float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis)); if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) { minAngle = - minAngle; @@ -550,8 +597,8 @@ void AnimInverseKinematics::initConstraints() { // these directions are approximate swing limits in parent-frame // NOTE: they don't need to be normalized std::vector swungDirections; - swungDirections.push_back(yAxis); - swungDirections.push_back(xAxis); + swungDirections.push_back(Vectors::UNIT_Y); + swungDirections.push_back(Vectors::UNIT_X); swungDirections.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); swungDirections.push_back(glm::vec3(1.0f, 1.0f, -1.0f)); @@ -575,13 +622,10 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele AnimNode::setSkeletonInternal(skeleton); // invalidate all targetVars - for (auto& targetVar : _targetVarVec) { - targetVar.hasPerformedJointLookup = false; + for (auto& targetVar: _targetVarVec) { + targetVar.jointIndex = -1; } - // invalidate all targets - _absoluteTargets.clear(); - _maxTargetIndex = 0; if (skeleton) { diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 41e380f64c..b59fb4d5fc 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -19,7 +19,7 @@ class RotationConstraint; class AnimInverseKinematics : public AnimNode { public: - AnimInverseKinematics(const std::string& id); + AnimInverseKinematics(const QString& id); virtual ~AnimInverseKinematics() override; void loadDefaultPoses(const AnimPoseVec& poses); @@ -45,28 +45,22 @@ protected: void initConstraints(); struct IKTargetVar { - IKTargetVar(const QString& jointNameIn, const std::string& positionVarIn, const std::string& rotationVarIn) : + IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn) : positionVar(positionVarIn), rotationVar(rotationVarIn), jointName(jointNameIn), jointIndex(-1), - hasPerformedJointLookup(false) {} + rootIndex(-1) {} - std::string positionVar; - std::string rotationVar; + QString positionVar; + QString rotationVar; QString jointName; int jointIndex; // cached joint index - bool hasPerformedJointLookup = false; - }; - - struct IKTarget { - AnimPose pose; - int rootIndex; + int rootIndex; // cached root index }; std::map _constraints; std::vector _targetVarVec; - std::map _absoluteTargets; // IK targets of end-points AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _relativePoses; // current relative poses diff --git a/libraries/animation/src/AnimController.cpp b/libraries/animation/src/AnimManipulator.cpp similarity index 54% rename from libraries/animation/src/AnimController.cpp rename to libraries/animation/src/AnimManipulator.cpp index c021124732..0640c418e3 100644 --- a/libraries/animation/src/AnimController.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -1,5 +1,5 @@ // -// AnimController.cpp +// AnimManipulator.cpp // // Created by Anthony J. Thibault on 9/8/15. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. @@ -8,77 +8,81 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AnimController.h" +#include "AnimManipulator.h" +#include "AnimUtil.h" #include "AnimationLogging.h" -AnimController::AnimController(const std::string& id, float alpha) : - AnimNode(AnimNode::Type::Controller, id), +AnimManipulator::AnimManipulator(const QString& id, float alpha) : + AnimNode(AnimNode::Type::Manipulator, id), _alpha(alpha) { } -AnimController::~AnimController() { +AnimManipulator::~AnimManipulator() { } -const AnimPoseVec& AnimController::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses()); } -const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { _alpha = animVars.lookup(_alphaVar, _alpha); for (auto& jointVar : _jointVars) { if (!jointVar.hasPerformedJointLookup) { - QString qJointName = QString::fromStdString(jointVar.jointName); - jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName); + jointVar.jointIndex = _skeleton->nameToJointIndex(jointVar.jointName); if (jointVar.jointIndex < 0) { - qCWarning(animation) << "AnimController could not find jointName" << qJointName << "in skeleton"; + qCWarning(animation) << "AnimManipulator could not find jointName" << jointVar.jointName << "in skeleton"; } jointVar.hasPerformedJointLookup = true; } if (jointVar.jointIndex >= 0) { - AnimPose defaultPose; - glm::quat absRot; - glm::quat parentAbsRot; + AnimPose defaultAbsPose; + AnimPose defaultRelPose; + AnimPose parentAbsPose = AnimPose::identity; if (jointVar.jointIndex <= (int)underPoses.size()) { // jointVar is an absolute rotation, if it is not set we will use the underPose as our default value - defaultPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); - absRot = animVars.lookup(jointVar.var, defaultPose.rot); + defaultRelPose = underPoses[jointVar.jointIndex]; + defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); + defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); if (parentIndex >= 0) { - parentAbsRot = _skeleton->getAbsolutePose(parentIndex, underPoses).rot; + parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses); } } else { // jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value - defaultPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); - absRot = animVars.lookup(jointVar.var, defaultPose.rot); + defaultRelPose = AnimPose::identity; + defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); + defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose // here we use the bind pose int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); if (parentIndex >= 0) { - parentAbsRot = _skeleton->getAbsoluteBindPose(parentIndex).rot; + parentAbsPose = _skeleton->getAbsoluteBindPose(parentIndex); } } // convert from absolute to relative - glm::quat relRot = glm::inverse(parentAbsRot) * absRot; - _poses[jointVar.jointIndex] = AnimPose(defaultPose.scale, relRot, defaultPose.trans); + AnimPose relPose = parentAbsPose.inverse() * defaultAbsPose; + + // blend with underPose + ::blend(1, &defaultRelPose, &relPose, _alpha, &_poses[jointVar.jointIndex]); } } return _poses; } -void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { +void AnimManipulator::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { AnimNode::setSkeletonInternal(skeleton); // invalidate all jointVar indices @@ -97,10 +101,10 @@ void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { } // for AnimDebugDraw rendering -const AnimPoseVec& AnimController::getPosesInternal() const { +const AnimPoseVec& AnimManipulator::getPosesInternal() const { return _poses; } -void AnimController::addJointVar(const JointVar& jointVar) { +void AnimManipulator::addJointVar(const JointVar& jointVar) { _jointVars.push_back(jointVar); } diff --git a/libraries/animation/src/AnimController.h b/libraries/animation/src/AnimManipulator.h similarity index 61% rename from libraries/animation/src/AnimController.h rename to libraries/animation/src/AnimManipulator.h index 15805d72fd..eca1a4aa71 100644 --- a/libraries/animation/src/AnimController.h +++ b/libraries/animation/src/AnimManipulator.h @@ -1,5 +1,5 @@ // -// AnimController.h +// AnimManipulator.h // // Created by Anthony J. Thibault on 9/8/15. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. @@ -8,31 +8,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_AnimController_h -#define hifi_AnimController_h +#ifndef hifi_AnimManipulator_h +#define hifi_AnimManipulator_h #include "AnimNode.h" // Allows procedural control over a set of joints. -class AnimController : public AnimNode { +class AnimManipulator : public AnimNode { public: friend class AnimTests; - AnimController(const std::string& id, float alpha); - virtual ~AnimController() override; + AnimManipulator(const QString& id, float alpha); + virtual ~AnimManipulator() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; - void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; struct JointVar { - JointVar(const std::string& varIn, const std::string& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {} - std::string var = ""; - std::string jointName = ""; + JointVar(const QString& varIn, const QString& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {} + QString var = ""; + QString jointName = ""; int jointIndex = -1; bool hasPerformedJointLookup = false; }; @@ -45,14 +45,14 @@ protected: AnimPoseVec _poses; float _alpha; - std::string _alphaVar; + QString _alphaVar; std::vector _jointVars; // no copies - AnimController(const AnimController&) = delete; - AnimController& operator=(const AnimController&) = delete; + AnimManipulator(const AnimManipulator&) = delete; + AnimManipulator& operator=(const AnimManipulator&) = delete; }; -#endif // hifi_AnimController_h +#endif // hifi_AnimManipulator_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index f994867181..9325ef3835 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -40,22 +40,22 @@ public: BlendLinear, Overlay, StateMachine, - Controller, + Manipulator, InverseKinematics, NumTypes }; using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - using Triggers = std::vector; + using Triggers = std::vector; friend class AnimDebugDraw; - friend void buildChildMap(std::map& map, Pointer node); + friend void buildChildMap(std::map& map, Pointer node); friend class AnimStateMachine; - AnimNode(Type type, const std::string& id) : _type(type), _id(id) {} + AnimNode(Type type, const QString& id) : _type(type), _id(id) {} virtual ~AnimNode() {} - const std::string& getID() const { return _id; } + const QString& getID() const { return _id; } Type getType() const { return _type; } // hierarchy accessors @@ -105,7 +105,7 @@ protected: virtual const AnimPoseVec& getPosesInternal() const = 0; Type _type; - std::string _id; + QString _id; std::vector _children; AnimSkeleton::ConstPointer _skeleton; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 9d6eb12e44..b2afae4728 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -20,7 +20,7 @@ #include "AnimOverlay.h" #include "AnimNodeLoader.h" #include "AnimStateMachine.h" -#include "AnimController.h" +#include "AnimManipulator.h" #include "AnimInverseKinematics.h" using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); @@ -31,7 +31,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); // called after children have been loaded @@ -40,7 +40,7 @@ static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static bool processControllerNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static const char* animNodeTypeToString(AnimNode::Type type) { @@ -49,7 +49,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return "blendLinear"; case AnimNode::Type::Overlay: return "overlay"; case AnimNode::Type::StateMachine: return "stateMachine"; - case AnimNode::Type::Controller: return "controller"; + case AnimNode::Type::Manipulator: return "manipulator"; case AnimNode::Type::InverseKinematics: return "inverseKinematics"; case AnimNode::Type::NumTypes: return nullptr; }; @@ -62,7 +62,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return loadBlendLinearNode; case AnimNode::Type::Overlay: return loadOverlayNode; case AnimNode::Type::StateMachine: return loadStateMachineNode; - case AnimNode::Type::Controller: return loadControllerNode; + case AnimNode::Type::Manipulator: return loadManipulatorNode; case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode; case AnimNode::Type::NumTypes: return nullptr; }; @@ -75,7 +75,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { case AnimNode::Type::BlendLinear: return processBlendLinearNode; case AnimNode::Type::Overlay: return processOverlayNode; case AnimNode::Type::StateMachine: return processStateMachineNode; - case AnimNode::Type::Controller: return processControllerNode; + case AnimNode::Type::Manipulator: return processManipulatorNode; case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode; case AnimNode::Type::NumTypes: return nullptr; }; @@ -122,7 +122,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { static AnimNode::Type stringToEnum(const QString& str) { // O(n), move to map when number of types becomes large. const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); - for (int i = 0; i < NUM_TYPES; i++ ) { + for (int i = 0; i < NUM_TYPES; i++) { AnimNode::Type type = static_cast(i); if (str == animNodeTypeToString(type)) { return type; @@ -200,19 +200,19 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_OPTIONAL_STRING(timeScaleVar, jsonObj); READ_OPTIONAL_STRING(loopFlagVar, jsonObj); - auto node = std::make_shared(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag); + auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag); if (!startFrameVar.isEmpty()) { - node->setStartFrameVar(startFrameVar.toStdString()); + node->setStartFrameVar(startFrameVar); } if (!endFrameVar.isEmpty()) { - node->setEndFrameVar(endFrameVar.toStdString()); + node->setEndFrameVar(endFrameVar); } if (!timeScaleVar.isEmpty()) { - node->setTimeScaleVar(timeScaleVar.toStdString()); + node->setTimeScaleVar(timeScaleVar); } if (!loopFlagVar.isEmpty()) { - node->setLoopFlagVar(loopFlagVar.toStdString()); + node->setLoopFlagVar(loopFlagVar); } return node; @@ -224,10 +224,10 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id.toStdString(), alpha); + auto node = std::make_shared(id, alpha); if (!alphaVar.isEmpty()) { - node->setAlphaVar(alphaVar.toStdString()); + node->setAlphaVar(alphaVar); } return node; @@ -271,31 +271,31 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri READ_OPTIONAL_STRING(boneSetVar, jsonObj); READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id.toStdString(), boneSetEnum, alpha); + auto node = std::make_shared(id, boneSetEnum, alpha); if (!boneSetVar.isEmpty()) { - node->setBoneSetVar(boneSetVar.toStdString()); + node->setBoneSetVar(boneSetVar); } if (!alphaVar.isEmpty()) { - node->setAlphaVar(alphaVar.toStdString()); + node->setAlphaVar(alphaVar); } return node; } static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { - auto node = std::make_shared(id.toStdString()); + auto node = std::make_shared(id); return node; } -static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { +static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); - auto node = std::make_shared(id.toStdString(), alpha); + auto node = std::make_shared(id, alpha); READ_OPTIONAL_STRING(alphaVar, jsonObj); if (!alphaVar.isEmpty()) { - node->setAlphaVar(alphaVar.toStdString()); + node->setAlphaVar(alphaVar); } auto jointsValue = jsonObj.value("joints"); @@ -315,7 +315,7 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS READ_STRING(var, jointObj, id, jsonUrl, nullptr); READ_STRING(jointName, jointObj, id, jsonUrl, nullptr); - AnimController::JointVar jointVar(var.toStdString(), jointName.toStdString()); + AnimManipulator::JointVar jointVar(var, jointName); node->addJointVar(jointVar); }; @@ -323,7 +323,7 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS } AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { - auto node = std::make_shared(id.toStdString()); + auto node = std::make_shared(id); auto targetsValue = jsonObj.value("targets"); if (!targetsValue.isArray()) { @@ -349,9 +349,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS return node; } -void buildChildMap(std::map& map, AnimNode::Pointer node) { +void buildChildMap(std::map& map, AnimNode::Pointer node) { for ( auto child : node->_children ) { - map.insert(std::pair(child->_id, child)); + map.insert(std::pair(child->_id, child)); } } @@ -368,15 +368,15 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, } // build a map for all children by name. - std::map childMap; + std::map childMap; buildChildMap(childMap, node); // first pass parse all the states and build up the state and transition map. - using StringPair = std::pair; + using StringPair = std::pair; using TransitionMap = std::multimap; TransitionMap transitionMap; - using StateMap = std::map; + using StateMap = std::map; StateMap stateMap; auto statesArray = statesValue.toArray(); @@ -394,22 +394,20 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, READ_OPTIONAL_STRING(interpTargetVar, stateObj); READ_OPTIONAL_STRING(interpDurationVar, stateObj); - auto stdId = id.toStdString(); - - auto iter = childMap.find(stdId); + auto iter = childMap.find(id); if (iter == childMap.end()) { qCCritical(animation) << "AnimNodeLoader, could not find stateMachine child (state) with nodeId =" << nodeId << "stateId =" << id << "url =" << jsonUrl.toDisplayString(); return false; } - auto statePtr = std::make_shared(stdId, iter->second, interpTarget, interpDuration); + auto statePtr = std::make_shared(id, iter->second, interpTarget, interpDuration); assert(statePtr); if (!interpTargetVar.isEmpty()) { - statePtr->setInterpTargetVar(interpTargetVar.toStdString()); + statePtr->setInterpTargetVar(interpTargetVar); } if (!interpDurationVar.isEmpty()) { - statePtr->setInterpDurationVar(interpDurationVar.toStdString()); + statePtr->setInterpDurationVar(interpDurationVar); } smNode->addState(statePtr); @@ -432,7 +430,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, READ_STRING(var, transitionObj, nodeId, jsonUrl, false); READ_STRING(state, transitionObj, nodeId, jsonUrl, false); - transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var.toStdString(), state.toStdString()))); + transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var, state))); } } @@ -443,12 +441,12 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, if (iter != stateMap.end()) { srcState->addTransition(AnimStateMachine::State::Transition(transition.second.first, iter->second)); } else { - qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id.c_str() << "dstState =" << transition.second.second.c_str() << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString(); + qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id << "dstState =" << transition.second.second << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString(); return false; } } - auto iter = stateMap.find(currentState.toStdString()); + auto iter = stateMap.find(currentState); if (iter == stateMap.end()) { qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId << "url = " << jsonUrl.toDisplayString(); } diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 1a0a16ca8a..08c4304b08 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -12,7 +12,7 @@ #include "AnimUtil.h" #include -AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) : +AnimOverlay::AnimOverlay(const QString& id, BoneSet boneSet, float alpha) : AnimNode(AnimNode::Type::Overlay, id), _boneSet(boneSet), _alpha(alpha) { } @@ -173,8 +173,8 @@ void AnimOverlay::buildEmptyBoneSet() { void AnimOverlay::buildLeftHandBoneSet() { assert(_skeleton); buildEmptyBoneSet(); - int headJoint = _skeleton->nameToJointIndex("LeftHand"); - for_each_child_joint(_skeleton, headJoint, [&](int i) { + int handJoint = _skeleton->nameToJointIndex("LeftHand"); + for_each_child_joint(_skeleton, handJoint, [&](int i) { _boneSetVec[i] = 1.0f; }); } @@ -182,8 +182,8 @@ void AnimOverlay::buildLeftHandBoneSet() { void AnimOverlay::buildRightHandBoneSet() { assert(_skeleton); buildEmptyBoneSet(); - int headJoint = _skeleton->nameToJointIndex("RightHand"); - for_each_child_joint(_skeleton, headJoint, [&](int i) { + int handJoint = _skeleton->nameToJointIndex("RightHand"); + for_each_child_joint(_skeleton, handJoint, [&](int i) { _boneSetVec[i] = 1.0f; }); } diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 2a87c54997..eda8847d40 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -40,13 +40,13 @@ public: NumBoneSets }; - AnimOverlay(const std::string& id, BoneSet boneSet, float alpha); + AnimOverlay(const QString& id, BoneSet boneSet, float alpha); virtual ~AnimOverlay() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; - void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; } - void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; } + void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; } + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } protected: void buildBoneSet(BoneSet boneSet); @@ -60,8 +60,8 @@ public: float _alpha; std::vector _boneSetVec; - std::string _boneSetVar; - std::string _alphaVar; + QString _boneSetVar; + QString _alphaVar; void buildFullBodyBoneSet(); void buildUpperBodyBoneSet(); diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 5de379dd33..8ce0fc95b0 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -12,7 +12,7 @@ #include "AnimUtil.h" #include "AnimationLogging.h" -AnimStateMachine::AnimStateMachine(const std::string& id) : +AnimStateMachine::AnimStateMachine(const QString& id) : AnimNode(AnimNode::Type::StateMachine, id) { } @@ -23,7 +23,7 @@ AnimStateMachine::~AnimStateMachine() { const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { - std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); + QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); if (_currentState->getID() != desiredStateID) { // switch states bool foundState = false; @@ -35,7 +35,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl } } if (!foundState) { - qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID.c_str() << ", referenced by _currentStateVar =" << _currentStateVar.c_str(); + qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID << ", referenced by _currentStateVar =" << _currentStateVar; } } @@ -77,7 +77,7 @@ void AnimStateMachine::addState(State::Pointer state) { void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) { - qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID().c_str() << "->" << desiredState->getID().c_str(); + qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID(); const float FRAMES_PER_SECOND = 30.0f; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index f2d941a568..cb6c99f067 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -49,23 +49,23 @@ protected: class Transition { public: friend AnimStateMachine; - Transition(const std::string& var, State::Pointer state) : _var(var), _state(state) {} + Transition(const QString& var, State::Pointer state) : _var(var), _state(state) {} protected: - std::string _var; + QString _var; State::Pointer _state; }; - State(const std::string& id, AnimNode::Pointer node, float interpTarget, float interpDuration) : + State(const QString& id, AnimNode::Pointer node, float interpTarget, float interpDuration) : _id(id), _node(node), _interpTarget(interpTarget), _interpDuration(interpDuration) {} - void setInterpTargetVar(const std::string& interpTargetVar) { _interpTargetVar = interpTargetVar; } - void setInterpDurationVar(const std::string& interpDurationVar) { _interpDurationVar = interpDurationVar; } + void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; } + void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; } AnimNode::Pointer getNode() const { return _node; } - const std::string& getID() const { return _id; } + const QString& getID() const { return _id; } protected: @@ -74,13 +74,13 @@ protected: void addTransition(const Transition& transition) { _transitions.push_back(transition); } - std::string _id; + QString _id; AnimNode::Pointer _node; float _interpTarget; // frames float _interpDuration; // frames - std::string _interpTargetVar; - std::string _interpDurationVar; + QString _interpTargetVar; + QString _interpDurationVar; std::vector _transitions; @@ -92,12 +92,12 @@ protected: public: - AnimStateMachine(const std::string& id); + AnimStateMachine(const QString& id); virtual ~AnimStateMachine() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; - void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; } + void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; } protected: @@ -123,7 +123,7 @@ protected: State::Pointer _currentState; std::vector _states; - std::string _currentStateVar; + QString _currentStateVar; private: // no copies diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index ac84aafc32..de224f936a 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -37,7 +37,7 @@ public: AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast(&_val) = value; } AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast(&_val) = value; } AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast(&_val) = value; } - AnimVariant(const std::string& value) : _type(Type::String) { _stringVal = value; } + AnimVariant(const QString& value) : _type(Type::String) { _stringVal = value; } bool isBool() const { return _type == Type::Bool; } bool isInt() const { return _type == Type::Int; } @@ -53,7 +53,7 @@ public: void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast(&_val) = value; } void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast(&_val) = value; } void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast(&_val) = value; } - void setString(const std::string& value) { assert(_type == Type::String); _stringVal = value; } + void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } int getInt() const { assert(_type == Type::Int); return _val.intVal; } @@ -61,11 +61,11 @@ public: const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast(&_val); } - const std::string& getString() const { assert(_type == Type::String); return _stringVal; } + const QString& getString() const { assert(_type == Type::String); return _stringVal; } protected: Type _type; - std::string _stringVal; + QString _stringVal; union { bool boolVal; int intVal; @@ -76,9 +76,9 @@ protected: class AnimVariantMap { public: - bool lookup(const std::string& key, bool defaultValue) const { + bool lookup(const QString& key, bool defaultValue) const { // check triggers first, then map - if (key.empty()) { + if (key.isEmpty()) { return defaultValue; } else if (_triggers.find(key) != _triggers.end()) { return true; @@ -88,8 +88,8 @@ public: } } - int lookup(const std::string& key, int defaultValue) const { - if (key.empty()) { + int lookup(const QString& key, int defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -97,8 +97,8 @@ public: } } - float lookup(const std::string& key, float defaultValue) const { - if (key.empty()) { + float lookup(const QString& key, float defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -106,8 +106,8 @@ public: } } - const glm::vec3& lookup(const std::string& key, const glm::vec3& defaultValue) const { - if (key.empty()) { + const glm::vec3& lookup(const QString& key, const glm::vec3& defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -115,8 +115,8 @@ public: } } - const glm::quat& lookup(const std::string& key, const glm::quat& defaultValue) const { - if (key.empty()) { + const glm::quat& lookup(const QString& key, const glm::quat& defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -124,8 +124,8 @@ public: } } - const glm::mat4& lookup(const std::string& key, const glm::mat4& defaultValue) const { - if (key.empty()) { + const glm::mat4& lookup(const QString& key, const glm::mat4& defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -133,8 +133,8 @@ public: } } - const std::string& lookup(const std::string& key, const std::string& defaultValue) const { - if (key.empty()) { + const QString& lookup(const QString& key, const QString& defaultValue) const { + if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); @@ -142,21 +142,23 @@ public: } } - void set(const std::string& key, bool value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, int value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, float value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } - void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); } - void unset(const std::string& key) { _map.erase(key); } + void set(const QString& key, bool value) { _map[key] = AnimVariant(value); } + void set(const QString& key, int value) { _map[key] = AnimVariant(value); } + void set(const QString& key, float value) { _map[key] = AnimVariant(value); } + void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } + void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); } + void set(const QString& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } + void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } + void unset(const QString& key) { _map.erase(key); } - void setTrigger(const std::string& key) { _triggers.insert(key); } + void setTrigger(const QString& key) { _triggers.insert(key); } void clearTriggers() { _triggers.clear(); } + bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } + protected: - std::map _map; - std::set _triggers; + std::map _map; + std::set _triggers; }; #endif // hifi_AnimVariant_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 076baf92c5..025cb5f3d1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -735,21 +735,12 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q return; } - if (freeLineage.isEmpty()) { + if (_enableAnimGraph && _animSkeleton) { + // the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit return; } - int numFree = freeLineage.size(); - if (_enableAnimGraph && _animSkeleton) { - if (endIndex == _leftHandJointIndex) { - auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; - _animVars.set("leftHandPosition", targetPosition + rootTrans); - _animVars.set("leftHandRotation", targetRotation); - } else if (endIndex == _rightHandJointIndex) { - auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; - _animVars.set("rightHandPosition", targetPosition + rootTrans); - _animVars.set("rightHandRotation", targetRotation); - } + if (freeLineage.isEmpty()) { return; } @@ -768,6 +759,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q // relax toward default rotation // NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax + int numFree = freeLineage.size(); for (int j = 0; j < numFree; j++) { int nextIndex = freeLineage.at(j); JointState& nextState = _jointStates[nextIndex]; @@ -995,13 +987,9 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS)); _animVars.set("headRotation", realLocalHeadOrientation); - auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; - - if (params.isInHMD) { - _animVars.set("headPosition", params.localHeadPosition + rootTrans); - } else { - _animVars.unset("headPosition"); - } + // There's a theory that when not in hmd, we should _animVars.unset("headPosition"). + // However, until that works well, let's always request head be positioned where requested by hmd, camera, or default. + _animVars.set("headPosition", params.localHeadPosition); } else if (!_enableAnimGraph) { auto& state = _jointStates[index]; @@ -1052,6 +1040,23 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (_enableAnimGraph && _animSkeleton) { + // TODO: figure out how to obtain the yFlip from where it is actually stored + glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + if (params.isLeftEnabled) { + _animVars.set("leftHandPosition", yFlipHACK * params.leftPosition); + _animVars.set("leftHandRotation", yFlipHACK * params.leftOrientation); + } else { + _animVars.unset("leftHandPosition"); + _animVars.unset("leftHandRotation"); + } + if (params.isRightEnabled) { + _animVars.set("rightHandPosition", yFlipHACK * params.rightPosition); + _animVars.set("rightHandRotation", yFlipHACK * params.rightOrientation); + } else { + _animVars.unset("rightHandPosition"); + _animVars.unset("rightHandRotation"); + } + // set leftHand grab vars _animVars.set("isLeftHandIdle", false); _animVars.set("isLeftHandPoint", false); diff --git a/libraries/audio/src/AudioBuffer.h b/libraries/audio/src/AudioBuffer.h index 558d686861..2d23be864b 100644 --- a/libraries/audio/src/AudioBuffer.h +++ b/libraries/audio/src/AudioBuffer.h @@ -239,7 +239,7 @@ inline void AudioFrameBuffer< T >::copyFrames(uint32_t channelCount, const uint3 if(typeid(T) == typeid(float32_t) && typeid(S) == typeid(int16_t)) { // source and destination aare not the same, convert from float32_t to int16_t and copy out - const int scale = (2 << ((8 * sizeof(S)) - 1)); + const int scale = (1 << ((8 * sizeof(S)) - 1)); if (frameAlignment16 && (_channelCount == 1 || _channelCount == 2)) { @@ -382,7 +382,7 @@ inline void AudioFrameBuffer< T >::copyFrames(uint32_t channelCount, const uint3 if(typeid(T) == typeid(float32_t) && typeid(S) == typeid(int16_t)) { // source and destination aare not the same, convert from int16_t to float32_t and copy in - const int scale = (2 << ((8 * sizeof(S)) - 1)); + const int scale = (1 << ((8 * sizeof(S)) - 1)); if (frameAlignment16 && (_channelCount == 1 || _channelCount == 2)) { diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 598e78e500..4af45d299b 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -16,6 +16,7 @@ #include "openvr/OpenVrDisplayPlugin.h" #include "oculus/OculusDisplayPlugin.h" +#include "oculus/OculusDebugDisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h" const QString& DisplayPlugin::MENU_PATH() { @@ -42,6 +43,10 @@ DisplayPluginList getDisplayPlugins() { // Windows Oculus SDK new OculusDisplayPlugin(), + // Windows Oculus Simulator... uses head tracking and the same rendering + // as the connected hardware, but without using the SDK to display to the + // Rift. Useful for debugging Rift performance with nSight. + new OculusDebugDisplayPlugin(), // Mac/Linux Oculus SDK (0.5) new OculusLegacyDisplayPlugin(), #ifdef Q_OS_WIN diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp new file mode 100644 index 0000000000..fa9d09e392 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp @@ -0,0 +1,151 @@ +// +// Created by Bradley Austin Davis on 2014/04/13. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "OculusBaseDisplayPlugin.h" + +#include + +#include "OculusHelpers.h" + +uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const { + return _desiredFramebufferSize; +} + +void OculusBaseDisplayPlugin::preRender() { +#if (OVR_MAJOR_VERSION >= 6) + ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex); + _trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds); + ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses); +#endif +} + +glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { + return _eyeProjections[eye]; +} + +void OculusBaseDisplayPlugin::resetSensors() { +#if (OVR_MAJOR_VERSION >= 6) + ovr_RecenterPose(_hmd); +#endif +} + +glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const { + return toGlm(_eyePoses[eye]); +} + +glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { + return toGlm(_trackingState.HeadPose.ThePose); +} + +bool OculusBaseDisplayPlugin::isSupported() const { +#if (OVR_MAJOR_VERSION >= 6) + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + return false; + } + bool result = false; + if (ovrHmd_Detect() > 0) { + result = true; + } + ovr_Shutdown(); + return result; +#else + return false; +#endif +} + +void OculusBaseDisplayPlugin::init() { +} + +void OculusBaseDisplayPlugin::deinit() { +} + +void OculusBaseDisplayPlugin::activate() { +#if (OVR_MAJOR_VERSION >= 6) + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + qFatal("Could not init OVR"); + } + +#if (OVR_MAJOR_VERSION == 6) + if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) { +#elif (OVR_MAJOR_VERSION == 7) + if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) { +#endif + Q_ASSERT(false); + qFatal("Failed to acquire HMD"); + } + + _hmdDesc = ovr_GetHmdDesc(_hmd); + + _ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd); + + glm::uvec2 eyeSizes[2]; + ovr_for_each_eye([&](ovrEyeType eye) { + _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; + ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + ovrPerspectiveProjection = + ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); + _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); + + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); + }); + ovrFovPort combined = _eyeFovs[Left]; + combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); + combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); + ovrMatrix4f ovrPerspectiveProjection = + ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); + + + + _desiredFramebufferSize = uvec2( + eyeSizes[0].x + eyeSizes[1].x, + std::max(eyeSizes[0].y, eyeSizes[1].y)); + + _frameIndex = 0; + + if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { + qFatal("Could not attach to sensor device"); + } + + // Parent class relies on our _hmd intialization, so it must come after that. + memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov)); + _sceneLayer.Header.Type = ovrLayerType_EyeFov; + _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; + ovr_for_each_eye([&](ovrEyeType eye) { + ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; + ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f); + _sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; + }); + + if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { + qFatal("Could not attach to sensor device"); + } +#endif + + WindowOpenGLDisplayPlugin::activate(); +} + +void OculusBaseDisplayPlugin::deactivate() { + WindowOpenGLDisplayPlugin::deactivate(); + +#if (OVR_MAJOR_VERSION >= 6) + ovr_Destroy(_hmd); + _hmd = nullptr; + ovr_Shutdown(); +#endif +} + +void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { + ++_frameIndex; +} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h new file mode 100644 index 0000000000..12023db1ae --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h @@ -0,0 +1,79 @@ +// +// Created by Bradley Austin Davis on 2015/05/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include "../WindowOpenGLDisplayPlugin.h" + +#include + +#include + +class OculusBaseDisplayPlugin : public WindowOpenGLDisplayPlugin { +public: + virtual bool isSupported() const override; + + virtual void init() override final; + virtual void deinit() override final; + + virtual void activate() override; + virtual void deactivate() override; + + // Stereo specific methods + virtual bool isHmd() const override final { return true; } + virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; + virtual glm::uvec2 getRecommendedRenderSize() const override final; + virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); } + virtual void resetSensors() override final; + virtual glm::mat4 getEyePose(Eye eye) const override final; + virtual glm::mat4 getHeadPose() const override final; + +protected: + virtual void preRender() override final; + virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + +protected: + ovrPosef _eyePoses[2]; + + mat4 _eyeProjections[3]; + mat4 _compositeEyeProjections[2]; + uvec2 _desiredFramebufferSize; + ovrTrackingState _trackingState; + unsigned int _frameIndex{ 0 }; + +#if (OVR_MAJOR_VERSION >= 6) + ovrHmd _hmd; + float _ipd{ OVR_DEFAULT_IPD }; + ovrEyeRenderDesc _eyeRenderDescs[2]; + ovrVector3f _eyeOffsets[2]; + ovrFovPort _eyeFovs[2]; + ovrHmdDesc _hmdDesc; + ovrLayerEyeFov _sceneLayer; +#endif +#if (OVR_MAJOR_VERSION == 7) + ovrGraphicsLuid _luid; +#endif +}; + +#if (OVR_MAJOR_VERSION == 6) +#define ovr_Create ovrHmd_Create +#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL +#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL +#define ovr_Destroy ovrHmd_Destroy +#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet +#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture +#define ovr_GetFloat ovrHmd_GetFloat +#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize +#define ovr_GetFrameTiming ovrHmd_GetFrameTiming +#define ovr_GetTrackingState ovrHmd_GetTrackingState +#define ovr_GetRenderDesc ovrHmd_GetRenderDesc +#define ovr_RecenterPose ovrHmd_RecenterPose +#define ovr_SubmitFrame ovrHmd_SubmitFrame +#define ovr_ConfigureTracking ovrHmd_ConfigureTracking + +#define ovr_GetHmdDesc(X) *X +#endif diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.cpp new file mode 100644 index 0000000000..2021ce1c5a --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.cpp @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis on 2014/04/13. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "OculusDebugDisplayPlugin.h" +#include + +const QString OculusDebugDisplayPlugin::NAME("Oculus Rift (Simulator)"); + +const QString & OculusDebugDisplayPlugin::getName() const { + return NAME; +} + +static const QString DEBUG_FLAG("HIFI_DEBUG_OCULUS"); +static bool enableDebugOculus = QProcessEnvironment::systemEnvironment().contains("HIFI_DEBUG_OCULUS"); + +bool OculusDebugDisplayPlugin::isSupported() const { + if (!enableDebugOculus) { + return false; + } + return OculusBaseDisplayPlugin::isSupported(); +} + +void OculusDebugDisplayPlugin::customizeContext() { + WindowOpenGLDisplayPlugin::customizeContext(); + enableVsync(false); +} + +void OculusDebugDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { + WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize); + OculusBaseDisplayPlugin::display(finalTexture, sceneSize); +} + +void OculusDebugDisplayPlugin::finishFrame() { + swapBuffers(); + doneCurrent(); +}; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.h new file mode 100644 index 0000000000..d23c6ba567 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis on 2015/05/29 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include "OculusBaseDisplayPlugin.h" + +class OculusDebugDisplayPlugin : public OculusBaseDisplayPlugin { +public: + virtual const QString & getName() const override; + virtual bool isSupported() const override; + +protected: + virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + virtual void customizeContext() override; + // Do not perform swap in finish + virtual void finishFrame() override; + +private: + static const QString NAME; +}; + diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 2f4a9c93cf..58675eab4d 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -7,59 +7,10 @@ // #include "OculusDisplayPlugin.h" -#include - -#include #include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#if defined(__GNUC__) && !defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdouble-promotion" -#endif - -#include - -#if defined(__GNUC__) && !defined(__clang__) - #pragma GCC diagnostic pop -#endif - - -#include -#include -#include #include "OculusHelpers.h" -#if (OVR_MAJOR_VERSION == 6) -#define ovr_Create ovrHmd_Create -#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL -#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL -#define ovr_Destroy ovrHmd_Destroy -#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet -#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture -#define ovr_GetFloat ovrHmd_GetFloat -#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize -#define ovr_GetFrameTiming ovrHmd_GetFrameTiming -#define ovr_GetTrackingState ovrHmd_GetTrackingState -#define ovr_GetRenderDesc ovrHmd_GetRenderDesc -#define ovr_RecenterPose ovrHmd_RecenterPose -#define ovr_SubmitFrame ovrHmd_SubmitFrame -#define ovr_ConfigureTracking ovrHmd_ConfigureTracking - -#define ovr_GetHmdDesc(X) *X -#endif - #if (OVR_MAJOR_VERSION >= 6) // A base class for FBO wrappers that need to use the Oculus C @@ -180,160 +131,21 @@ private: const QString OculusDisplayPlugin::NAME("Oculus Rift"); -uvec2 OculusDisplayPlugin::getRecommendedRenderSize() const { - return _desiredFramebufferSize; -} - -void OculusDisplayPlugin::preRender() { -#if (OVR_MAJOR_VERSION >= 6) - ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex); - _trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds); - ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses); -#endif -} - -glm::mat4 OculusDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { - return _eyeProjections[eye]; -} - -void OculusDisplayPlugin::resetSensors() { -#if (OVR_MAJOR_VERSION >= 6) - ovr_RecenterPose(_hmd); -#endif -} - -glm::mat4 OculusDisplayPlugin::getEyePose(Eye eye) const { - return toGlm(_eyePoses[eye]); -} - -glm::mat4 OculusDisplayPlugin::getHeadPose() const { - return toGlm(_trackingState.HeadPose.ThePose); -} - const QString & OculusDisplayPlugin::getName() const { return NAME; } -bool OculusDisplayPlugin::isSupported() const { -#if (OVR_MAJOR_VERSION >= 6) - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { - return false; - } - bool result = false; - if (ovrHmd_Detect() > 0) { - result = true; - } - ovr_Shutdown(); - return result; -#else - return false; -#endif -} - -void OculusDisplayPlugin::init() { - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { - qFatal("Could not init OVR"); - } -} - -void OculusDisplayPlugin::deinit() { - ovr_Shutdown(); -} - -#if (OVR_MAJOR_VERSION >= 6) -ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() { - return _sceneLayer; -} -#endif - -//static gpu::TexturePointer _texture; - -void OculusDisplayPlugin::activate() { -#if (OVR_MAJOR_VERSION >= 6) - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { - Q_ASSERT(false); - qFatal("Failed to Initialize SDK"); - } - -// CONTAINER->getPrimarySurface()->makeCurrent(); -#if (OVR_MAJOR_VERSION == 6) - if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) { -#elif (OVR_MAJOR_VERSION == 7) - if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) { -#endif - Q_ASSERT(false); - qFatal("Failed to acquire HMD"); - } - - _hmdDesc = ovr_GetHmdDesc(_hmd); - - _ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd); - - glm::uvec2 eyeSizes[2]; - ovr_for_each_eye([&](ovrEyeType eye) { - _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; - ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); - ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); - _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); - - ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); - _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - - _eyeOffsets[eye] = erd.HmdToEyeViewOffset; - eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); - }); - ovrFovPort combined = _eyeFovs[Left]; - combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); - combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); - ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); - _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); - - - - _desiredFramebufferSize = uvec2( - eyeSizes[0].x + eyeSizes[1].x, - std::max(eyeSizes[0].y, eyeSizes[1].y)); - - _frameIndex = 0; - - if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { - qFatal("Could not attach to sensor device"); - } - - WindowOpenGLDisplayPlugin::activate(); - - // Parent class relies on our _hmd intialization, so it must come after that. - ovrLayerEyeFov& sceneLayer = getSceneLayer(); - memset(&sceneLayer, 0, sizeof(ovrLayerEyeFov)); - sceneLayer.Header.Type = ovrLayerType_EyeFov; - sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; - ovr_for_each_eye([&](ovrEyeType eye) { - ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; - ovrSizei & size = sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f); - sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; - }); - // We're rendering both eyes to the same texture, so only one of the - // pointers is populated - sceneLayer.ColorTexture[0] = _sceneFbo->color; - // not needed since the structure was zeroed on init, but explicit - sceneLayer.ColorTexture[1] = nullptr; - - if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { - qFatal("Could not attach to sensor device"); - } -#endif -} - void OculusDisplayPlugin::customizeContext() { WindowOpenGLDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); _sceneFbo->Init(getRecommendedRenderSize()); + + // We're rendering both eyes to the same texture, so only one of the + // pointers is populated + _sceneLayer.ColorTexture[0] = _sceneFbo->color; + // not needed since the structure was zeroed on init, but explicit + _sceneLayer.ColorTexture[1] = nullptr; #endif enableVsync(false); // Only enable mirroring if we know vsync is disabled @@ -345,13 +157,9 @@ void OculusDisplayPlugin::deactivate() { makeCurrent(); _sceneFbo.reset(); doneCurrent(); - - WindowOpenGLDisplayPlugin::deactivate(); - - ovr_Destroy(_hmd); - _hmd = nullptr; - ovr_Shutdown(); #endif + + OculusBaseDisplayPlugin::deactivate(); } void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { @@ -379,9 +187,8 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi drawUnitQuad(); }); - ovrLayerEyeFov& sceneLayer = getSceneLayer(); ovr_for_each_eye([&](ovrEyeType eye) { - sceneLayer.RenderPose[eye] = _eyePoses[eye]; + _sceneLayer.RenderPose[eye] = _eyePoses[eye]; }); auto windowSize = toGlm(_window->size()); @@ -391,7 +198,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0]; viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1]; - ovrLayerHeader* layers = &sceneLayer.Header; + ovrLayerHeader* layers = &_sceneLayer.Header; ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1); if (!OVR_SUCCESS(result)) { qDebug() << result; @@ -403,11 +210,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi #endif } -// Pass input events on to the application -bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { - return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); -} - /* The swapbuffer call here is only required if we want to mirror the content to the screen. However, it should only be done if we can reliably disable v-sync on the mirror surface, @@ -419,36 +221,3 @@ void OculusDisplayPlugin::finishFrame() { } doneCurrent(); }; - - -#if 0 -/* -An alternative way to render the UI is to pass it specifically as a composition layer to -the Oculus SDK which should technically result in higher quality. However, the SDK doesn't -have a mechanism to present the image as a sphere section, which is our desired look. -*/ -ovrLayerQuad& uiLayer = getUiLayer(); -if (nullptr == uiLayer.ColorTexture || overlaySize != _uiFbo->size) { - _uiFbo->Resize(overlaySize); - uiLayer.ColorTexture = _uiFbo->color; - uiLayer.Viewport.Size.w = overlaySize.x; - uiLayer.Viewport.Size.h = overlaySize.y; - float overlayAspect = aspect(overlaySize); - uiLayer.QuadSize.x = 1.0f; - uiLayer.QuadSize.y = 1.0f / overlayAspect; -} - -_uiFbo->Bound([&] { - Q_ASSERT(0 == glGetError()); - using namespace oglplus; - Context::Viewport(_uiFbo->size.x, _uiFbo->size.y); - glClearColor(0, 0, 0, 0); - Context::Clear().ColorBuffer(); - - _program->Bind(); - glBindTexture(GL_TEXTURE_2D, overlayTexture); - _plane->Use(); - _plane->Draw(); - Q_ASSERT(0 == glGetError()); -}); -#endif diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h index d30356daa0..7db83884cd 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -7,43 +7,17 @@ // #pragma once -#include "../WindowOpenGLDisplayPlugin.h" +#include "OculusBaseDisplayPlugin.h" -#include - -#include - -class OffscreenGlCanvas; struct SwapFramebufferWrapper; -struct MirrorFramebufferWrapper; - using SwapFboPtr = QSharedPointer; -using MirrorFboPtr = QSharedPointer; -class OculusDisplayPlugin : public WindowOpenGLDisplayPlugin { +class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: - virtual bool isSupported() const override; + virtual void deactivate() override; virtual const QString & getName() const override; - virtual void init() override; - virtual void deinit() override; - - virtual void activate() override; - virtual void deactivate() override; - - virtual bool eventFilter(QObject* receiver, QEvent* event) override; - - // Stereo specific methods - virtual bool isHmd() const override { return true; } - virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; - virtual glm::uvec2 getRecommendedRenderSize() const override; - virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } - virtual void resetSensors() override; - virtual glm::mat4 getEyePose(Eye eye) const override; - virtual glm::mat4 getHeadPose() const override; - protected: - virtual void preRender() override; virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; virtual void customizeContext() override; // Do not perform swap in finish @@ -51,30 +25,10 @@ protected: private: static const QString NAME; - - ovrPosef _eyePoses[2]; - - mat4 _eyeProjections[3]; - mat4 _compositeEyeProjections[2]; - uvec2 _desiredFramebufferSize; - ovrTrackingState _trackingState; bool _enableMirror{ false }; #if (OVR_MAJOR_VERSION >= 6) - ovrHmd _hmd; - float _ipd{ OVR_DEFAULT_IPD }; - unsigned int _frameIndex; - ovrEyeRenderDesc _eyeRenderDescs[2]; - ovrVector3f _eyeOffsets[2]; - ovrFovPort _eyeFovs[2]; - - ovrLayerEyeFov& getSceneLayer(); - ovrHmdDesc _hmdDesc; SwapFboPtr _sceneFbo; - ovrLayerEyeFov _sceneLayer; -#endif -#if (OVR_MAJOR_VERSION == 7) - ovrGraphicsLuid _luid; #endif }; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 95e0ef741d..fab9cc5dd4 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -161,7 +161,7 @@ void OpenVrDisplayPlugin::resetSensors() { } glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const { - return _eyesData[eye]._eyeOffset * getHeadPose(); + return getHeadPose() * _eyesData[eye]._eyeOffset; } glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 182681db79..f8930d59e0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -93,6 +93,15 @@ void EntityTreeRenderer::clear() { OctreeRenderer::clear(); } +void EntityTreeRenderer::reloadEntityScripts() { + _entitiesScriptEngine->unloadAllEntityScripts(); + foreach(auto entity, _entitiesInScene) { + if (!entity->getScript().isEmpty()) { + _entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true); + } + } +} + void EntityTreeRenderer::init() { OctreeRenderer::init(); EntityTreePointer entityTree = std::static_pointer_cast(_tree); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index a2f343efd2..18874957fd 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -62,6 +62,9 @@ public: /// clears the tree virtual void clear(); + /// reloads the entity scripts, calling unload and preload + void reloadEntityScripts(); + /// if a renderable entity item needs a model, we will allocate it for them Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 581eed184d..8d2b0b6892 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1,6 +1,6 @@ // // EntityScriptingInterface.h -// libraries/models/src +// libraries/entities/src // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 High Fidelity, Inc. diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index 42897e9947..177c798e2c 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -160,15 +160,22 @@ typedef std::vector< BufferPointer > Buffers; class BufferView { +protected: + void initFromBuffer(const BufferPointer& buffer) { + _buffer = (buffer); + if (_buffer) { + _size = (buffer->getSize()); + } + } public: typedef Resource::Size Size; typedef int Index; BufferPointer _buffer; - Size _offset; - Size _size; + Size _offset{ 0 }; + Size _size{ 0 }; Element _element; - uint16 _stride; + uint16 _stride{ 1 }; BufferView() : _buffer(NULL), @@ -188,19 +195,19 @@ public: // create the BufferView and own the Buffer BufferView(Buffer* newBuffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) : - _buffer(newBuffer), _offset(0), - _size(newBuffer->getSize()), _element(element), _stride(uint16(element.getSize())) - {}; + { + initFromBuffer(BufferPointer(newBuffer)); + }; BufferView(const BufferPointer& buffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) : - _buffer(buffer), _offset(0), - _size(buffer->getSize()), _element(element), _stride(uint16(element.getSize())) - {}; + { + initFromBuffer(buffer); + }; BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) : _buffer(buffer), _offset(offset), diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index c13bc07be1..87af2a5cf8 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -11,16 +11,21 @@ #include "AssetClient.h" -#include -#include - #include +#include +#include +#include +#include + #include "AssetRequest.h" #include "AssetUpload.h" +#include "AssetUtils.h" +#include "NetworkAccessManager.h" +#include "NetworkLogging.h" #include "NodeList.h" #include "PacketReceiver.h" -#include "AssetUtils.h" +#include "ResourceCache.h" MessageID AssetClient::_currentID = 0; @@ -40,9 +45,29 @@ AssetClient::AssetClient() { connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled); } +void AssetClient::init() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection); + } + + // Setup disk cache if not already + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + if (!networkAccessManager.cache()) { + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache"; + + QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); + cache->setCacheDirectory(cachePath); + networkAccessManager.setCache(cache); + qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath + << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; + } +} + AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { - qDebug() << "Invalid hash size"; + qCWarning(asset_client) << "Invalid hash size"; return nullptr; } @@ -50,7 +75,7 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (!assetServer) { - qDebug().nospace() << "Could not request " << hash << "." << extension + qCWarning(asset_client).nospace() << "Could not request " << hash << "." << extension << " since you are not currently connected to an asset-server."; return nullptr; } @@ -68,7 +93,7 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (!assetServer) { - qDebug() << "Could not upload" << filename << "since you are not currently connected to an asset-server."; + qCWarning(asset_client) << "Could not upload" << filename << "since you are not currently connected to an asset-server."; return nullptr; } @@ -82,7 +107,7 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { - qDebug() << "Invalid hash size"; + qCWarning(asset_client) << "Invalid hash size"; return false; } @@ -97,7 +122,7 @@ bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOf + sizeof(start) + sizeof(end); auto packet = NLPacket::create(PacketType::AssetGet, payloadSize, true); - qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server."; + qCDebug(asset_client) << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server."; packet->writePrimitive(messageID); @@ -184,7 +209,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer packetList, S packet.open(QIODevice::ReadOnly); auto assetHash = packet.read(SHA256_HASH_LENGTH); - qDebug() << "Got reply for asset: " << assetHash.toHex(); + qCDebug(asset_client) << "Got reply for asset: " << assetHash.toHex(); MessageID messageID; packet.read(reinterpret_cast(&messageID), sizeof(messageID)); @@ -198,7 +223,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer packetList, S packet.read(reinterpret_cast(&length), sizeof(DataOffset)); data = packet.read(length); } else { - qDebug() << "Failure getting asset: " << error; + qCWarning(asset_client) << "Failure getting asset: " << error; } // Check if we have any pending requests for this node @@ -254,15 +279,15 @@ void AssetClient::handleAssetUploadReply(QSharedPointer packet, Shared AssetServerError error; packet->readPrimitive(&error); - QString hashString { "" }; + QString hashString; if (error) { - qDebug() << "Error uploading file to asset server"; + qCWarning(asset_client) << "Error uploading file to asset server"; } else { auto hash = packet->read(SHA256_HASH_LENGTH); hashString = hash.toHex(); - qDebug() << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString; + qCDebug(asset_client) << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString; } // Check if we have any pending requests for this node diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 6557954467..9b82e63b58 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -40,6 +40,8 @@ class AssetClient : public QObject, public Dependency { Q_OBJECT public: AssetClient(); + + Q_INVOKABLE void init(); Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 45e925389d..b4e3882a5e 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -13,18 +13,20 @@ #include -#include +#include +#include #include "AssetClient.h" +#include "NetworkAccessManager.h" #include "NetworkLogging.h" #include "NodeList.h" +#include "ResourceCache.h" AssetRequest::AssetRequest(const QString& hash, const QString& extension) : QObject(), _hash(hash), _extension(extension) { - } void AssetRequest::start() { @@ -34,7 +36,19 @@ void AssetRequest::start() { } if (_state != NotStarted) { - qCWarning(networking) << "AssetRequest already started."; + qCWarning(asset_client) << "AssetRequest already started."; + return; + } + + // Try to load from cache + if (loadFromCache()) { + _info.hash = _hash; + _info.size = _data.size(); + _error = NoError; + + _state = Finished; + emit finished(this); + qCDebug(asset_client) << getUrl().toDisplayString() << "loaded from disk cache."; return; } @@ -58,8 +72,7 @@ void AssetRequest::start() { } if (_error != NoError) { - qCDebug(networking) << "Got error retrieving asset info for" << _hash; - + qCWarning(asset_client) << "Got error retrieving asset info for" << _hash; _state = Finished; emit finished(this); @@ -69,7 +82,7 @@ void AssetRequest::start() { _state = WaitingForData; _data.resize(info.size); - qCDebug(networking) << "Got size of " << _hash << " : " << info.size << " bytes"; + qCDebug(asset_client) << "Got size of " << _hash << " : " << info.size << " bytes"; int start = 0, end = _info.size; @@ -98,6 +111,10 @@ void AssetRequest::start() { memcpy(_data.data() + start, data.constData(), data.size()); _totalReceived += data.size(); emit progress(_totalReceived, _info.size); + + if (saveToCache(data)) { + qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache"; + } } else { // hash doesn't match - we have an error _error = HashVerificationFailed; @@ -106,7 +123,7 @@ void AssetRequest::start() { } if (_error != NoError) { - qCDebug(networking) << "Got error retrieving asset" << _hash << "- error code" << _error; + qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; } _state = Finished; @@ -114,3 +131,51 @@ void AssetRequest::start() { }); }); } + +QUrl AssetRequest::getUrl() const { + if (!_extension.isEmpty()) { + return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, _hash, _extension)); + } else { + return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, _hash)); + } +} + +bool AssetRequest::loadFromCache() { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + auto url = getUrl(); + if (auto ioDevice = cache->data(url)) { + _data = ioDevice->readAll(); + return true; + } else { + qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; + } + } else { + qCWarning(asset_client) << "No disk cache to load assets from."; + } + return false; +} + +bool AssetRequest::saveToCache(const QByteArray& file) const { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + auto url = getUrl(); + + if (!cache->metaData(url).isValid()) { + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + metaData.setSaveToDisk(true); + metaData.setLastModified(QDateTime::currentDateTime()); + metaData.setExpirationDate(QDateTime()); // Never expires + + if (auto ioDevice = cache->prepare(metaData)) { + ioDevice->write(file); + cache->insert(ioDevice); + return true; + } + qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; + } + } else { + qCWarning(asset_client) << "No disk cache to save assets to."; + } + return false; +} + diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 9bd224b35e..75e2353425 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -46,12 +46,16 @@ public: const QByteArray& getData() const { return _data; } const State& getState() const { return _state; } const Error& getError() const { return _error; } + QUrl getUrl() const; signals: void finished(AssetRequest* thisRequest); void progress(qint64 totalReceived, qint64 total); private: + bool loadFromCache(); + bool saveToCache(const QByteArray& file) const; + State _state = NotStarted; Error _error = NoError; AssetInfo _info; diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 6b419fc293..ecbe80cddb 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -32,7 +32,6 @@ void AssetResourceRequest::doSend() { _state = Finished; emit finished(); - return; } @@ -43,12 +42,11 @@ void AssetResourceRequest::doSend() { _state = Finished; emit finished(); - return; } connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress); - QObject::connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) mutable { + connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) { Q_ASSERT(_state == InProgress); Q_ASSERT(req == _assetRequest); Q_ASSERT(req->getState() == AssetRequest::Finished); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 9c9d172959..92632a43e5 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -15,6 +15,7 @@ #include #include "AssetClient.h" +#include "NetworkLogging.h" AssetUpload::AssetUpload(QObject* object, const QString& filename) : _filename(filename) @@ -41,7 +42,7 @@ void AssetUpload::start() { // ask the AssetClient to upload the asset and emit the proper signals from the passed callback auto assetClient = DependencyManager::get(); - qDebug() << "Attempting to upload" << _filename << "to asset-server."; + qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ if (!responseReceived) { diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index fd650df348..34c9c1dad2 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -29,8 +29,7 @@ HTTPResourceRequest::~HTTPResourceRequest() { } void HTTPResourceRequest::doSend() { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(_url); + QNetworkRequest networkRequest(_url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); if (_cacheEnabled) { @@ -39,7 +38,7 @@ void HTTPResourceRequest::doSend() { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); } - _reply = networkAccessManager.get(networkRequest); + _reply = NetworkAccessManager::getInstance().get(networkRequest); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress); diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 841b7491c7..97171d5ad7 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -18,6 +18,7 @@ QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { networkAccessManagers.setLocalData(new QNetworkAccessManager()); + } return *networkAccessManagers.localData(); diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h index 5a64f4ae0a..c4b435adb6 100644 --- a/libraries/networking/src/NetworkAccessManager.h +++ b/libraries/networking/src/NetworkAccessManager.h @@ -12,7 +12,7 @@ #ifndef hifi_NetworkAccessManager_h #define hifi_NetworkAccessManager_h -#include +#include /// Wrapper around QNetworkAccessManager to restrict at one instance by thread class NetworkAccessManager : public QObject { diff --git a/libraries/networking/src/NetworkLogging.cpp b/libraries/networking/src/NetworkLogging.cpp index 45ff716a97..28b209960e 100644 --- a/libraries/networking/src/NetworkLogging.cpp +++ b/libraries/networking/src/NetworkLogging.cpp @@ -12,3 +12,4 @@ #include "NetworkLogging.h" Q_LOGGING_CATEGORY(networking, "hifi.networking") +Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client") diff --git a/libraries/networking/src/NetworkLogging.h b/libraries/networking/src/NetworkLogging.h index 47a6a34264..838bbb57d2 100644 --- a/libraries/networking/src/NetworkLogging.h +++ b/libraries/networking/src/NetworkLogging.h @@ -15,5 +15,6 @@ #include Q_DECLARE_LOGGING_CATEGORY(networking) +Q_DECLARE_LOGGING_CATEGORY(asset_client) #endif // hifi_NetworkLogging_h diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 66950a60d0..e2f57f97a3 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 3bae7f5b9d..97e46f088a 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -35,6 +35,7 @@ class Resource; static const qint64 BYTES_PER_MEGABYTES = 1024 * 1024; static const qint64 BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; +static const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB // Windows can have troubles allocating that much memory in ram sometimes // so default cache size at 100 MB on windows (1GB otherwise) diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index aee2805f32..5f0db9412c 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -43,7 +43,9 @@ void ThreadedAssignment::setFinished(bool isFinished) { packetReceiver.setShouldDropPackets(true); if (_domainServerTimer) { - _domainServerTimer->stop(); + // stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread + _domainServerTimer->deleteLater(); + _domainServerTimer = nullptr; } if (_statsTimer) { @@ -65,9 +67,12 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy auto nodeList = DependencyManager::get(); nodeList->setOwnerType(nodeType); - _domainServerTimer = new QTimer(this); + _domainServerTimer = new QTimer; connect(_domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); _domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + + // move the domain server time to the NL so check-ins fire from there + _domainServerTimer->moveToThread(nodeList->thread()); if (shouldSendStats) { // start sending stats packet once we connect to the domain diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 2fb28f81ee..1bda840a6c 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -56,14 +56,15 @@ void Connection::stopSendQueue() { // grab the send queue thread so we can wait on it QThread* sendQueueThread = _sendQueue->thread(); - // since we're stopping the send queue we should consider our handshake ACK not receieved - _hasReceivedHandshakeACK = false; - // tell the send queue to stop and be deleted + _sendQueue->stop(); _sendQueue->deleteLater(); _sendQueue.release(); + // since we're stopping the send queue we should consider our handshake ACK not receieved + _hasReceivedHandshakeACK = false; + // wait on the send queue thread so we know the send queue is gone sendQueueThread->quit(); sendQueueThread->wait(); @@ -111,7 +112,7 @@ void Connection::queueInactive() { qCDebug(networking) << "Connection SendQueue to" << _destination << "stopped and no data is being received - stopping connection."; #endif - emit connectionInactive(_destination); + deactivate(); } } @@ -170,7 +171,9 @@ void Connection::sync() { qCDebug(networking) << "Connection to" << _destination << "no longer receiving any data and there is currently no send queue - stopping connection."; #endif - emit connectionInactive(_destination); + deactivate(); + + return; } } @@ -207,7 +210,9 @@ void Connection::sync() { << CONNECTION_NOT_USED_EXPIRY_SECONDS << "seconds - stopping connection."; #endif - emit connectionInactive(_destination); + deactivate(); + + return; } } } @@ -728,11 +733,14 @@ void Connection::processHandshake(std::unique_ptr controlPacket) } void Connection::processHandshakeACK(std::unique_ptr controlPacket) { - // hand off this handshake ACK to the send queue so it knows it can start sending - getSendQueue().handshakeACK(); - - // indicate that handshake ACK was received - _hasReceivedHandshakeACK = true; + // if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless + if (_sendQueue) { + // hand off this handshake ACK to the send queue so it knows it can start sending + getSendQueue().handshakeACK(); + + // indicate that handshake ACK was received + _hasReceivedHandshakeACK = true; + } } void Connection::processTimeoutNAK(std::unique_ptr controlPacket) { diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 2b1dec1ae9..13756c12f9 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -71,6 +71,8 @@ public: void queueReceivedMessagePacket(std::unique_ptr packet); ConnectionStats::Stats sampleStats() { return _stats.sample(); } + + bool isActive() const { return _isActive; } signals: void packetSent(); @@ -100,6 +102,8 @@ private: void resetReceiveState(); void resetRTT(); + void deactivate() { _isActive = false; emit connectionInactive(_destination); } + SendQueue& getSendQueue(); SequenceNumber nextACK() const; void updateRTT(int rtt); @@ -123,6 +127,7 @@ private: p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection + bool _isActive { true }; // flag used for inactivity of connection LossList _lossList; // List of all missing packets SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index a09ea6ca9a..ac6c8238e8 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -65,6 +65,7 @@ std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destin // Move queue to private thread and start it queue->moveToThread(thread); + thread->start(); return std::move(queue); @@ -89,7 +90,8 @@ void SendQueue::queuePacket(std::unique_ptr packet) { // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets _emptyCondition.notify_one(); } - if (!this->thread()->isRunning()) { + + if (!this->thread()->isRunning() && _state == State::NotStarted) { this->thread()->start(); } } @@ -135,14 +137,15 @@ void SendQueue::queuePacketList(std::unique_ptr packetList) { // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets _emptyCondition.notify_one(); } - - if (!this->thread()->isRunning()) { + + if (!this->thread()->isRunning() && _state == State::NotStarted) { this->thread()->start(); } } void SendQueue::stop() { - _isRunning = false; + + _state = State::Stopped; // in case we're waiting to send another handshake, release the condition_variable now so we cleanup sooner _handshakeACKCondition.notify_one(); @@ -268,9 +271,23 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, } void SendQueue::run() { - _isRunning = true; + if (_state == State::Stopped) { + // we've already been asked to stop before we even got a chance to start + // don't start now +#ifdef UDT_CONNECTION_DEBUG + qDebug() << "SendQueue asked to run after being told to stop. Will not run."; +#endif + return; + } else if (_state == State::Running) { +#ifdef UDT_CONNECTION_DEBUG + qDebug() << "SendQueue asked to run but is already running (according to state). Will not re-run."; +#endif + return; + } - while (_isRunning) { + _state = State::Running; + + while (_state == State::Running) { // Record how long the loop takes to execute auto loopStartTimestamp = p_high_resolution_clock::now(); @@ -314,11 +331,11 @@ void SendQueue::run() { } // since we're a while loop, give the thread a chance to process events - QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(this, 0); // we just processed events so check now if we were just told to stop - if (!_isRunning) { - break; + if (_state != State::Running) { + return; } if (_hasReceivedHandshakeACK && !sentAPacket) { @@ -525,5 +542,5 @@ void SendQueue::deactivate() { // this queue is inactive - emit that signal and stop the while emit queueInactive(); - _isRunning = false; + _state = State::Stopped; } diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 2e7ec90c45..88b6b045b0 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -45,6 +45,12 @@ class SendQueue : public QObject { Q_OBJECT public: + enum class State { + NotStarted, + Running, + Stopped + }; + static std::unique_ptr create(Socket* socket, HifiSockAddr destination); void queuePacket(std::unique_ptr packet); @@ -106,7 +112,7 @@ private: std::atomic _atomicCurrentSequenceNumber { 0 };// Atomic for last sequence number sent out std::atomic _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC - std::atomic _isRunning { false }; + std::atomic _state { State::NotStarted }; std::atomic _estimatedTimeout { 0 }; // Estimated timeout, set from CC std::atomic _timeoutExpiryCount { 0 }; // The number of times the timeout has expired without response from client diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 56a00c6808..50f2e67007 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -183,8 +183,7 @@ Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) { auto connection = std::unique_ptr(new Connection(this, sockAddr, _ccFactory->create())); // we queue the connection to cleanup connection in case it asks for it during its own rate control sync - QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection, - Qt::QueuedConnection); + QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection); #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "Creating new connection to" << sockAddr; @@ -208,11 +207,13 @@ void Socket::clearConnections() { } void Socket::cleanupConnection(HifiSockAddr sockAddr) { -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr; -#endif + auto numErased = _connectionsHash.erase(sockAddr); - _connectionsHash.erase(sockAddr); + if (numErased > 0) { +#ifdef UDT_CONNECTION_DEBUG + qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr; +#endif + } } void Socket::messageReceived(std::unique_ptr packetList) { @@ -297,8 +298,31 @@ void Socket::connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* r void Socket::rateControlSync() { // enumerate our list of connections and ask each of them to send off periodic ACK packet for rate control + + // the way we do this is a little funny looking - we need to avoid the case where we call sync and + // (because of our Qt direct connection to the Connection's signal that it has been deactivated) + // an iterator on _connectionsHash would be invalidated by our own call to cleanupConnection + + // collect the sockets for all connections in a vector + + std::vector sockAddrVector; + sockAddrVector.reserve(_connectionsHash.size()); + for (auto& connection : _connectionsHash) { - connection.second->sync(); + sockAddrVector.emplace_back(connection.first); + } + + // enumerate that vector of HifiSockAddr objects + for (auto& sockAddr : sockAddrVector) { + // pull out the respective connection via a quick find on the unordered_map + auto it = _connectionsHash.find(sockAddr); + + if (it != _connectionsHash.end()) { + // if the connection is erased while calling sync since we are re-using the iterator that was invalidated + // we're good to go + auto& connection = _connectionsHash[sockAddr]; + connection->sync(); + } } if (_synTimer->interval() != _synInterval) { diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 3a80180008..88a77a4c53 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -23,9 +23,6 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() : _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); } -OctreeHeadlessViewer::~OctreeHeadlessViewer() { -} - void OctreeHeadlessViewer::init() { OctreeRenderer::init(); setViewFrustum(&_viewFrustum); @@ -34,8 +31,9 @@ void OctreeHeadlessViewer::init() { void OctreeHeadlessViewer::queryOctree() { char serverType = getMyNodeType(); PacketType packetType = getMyQueryMessageType(); + NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions(); - + bool wantExtraDebugging = false; if (wantExtraDebugging) { diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index be076e7b5f..5a17f48a19 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -29,7 +29,7 @@ class OctreeHeadlessViewer : public OctreeRenderer { Q_OBJECT public: OctreeHeadlessViewer(); - virtual ~OctreeHeadlessViewer(); + virtual ~OctreeHeadlessViewer() {}; virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ } virtual void init(); @@ -65,7 +65,7 @@ public slots: private: ViewFrustum _viewFrustum; - JurisdictionListener* _jurisdictionListener; + JurisdictionListener* _jurisdictionListener = nullptr; OctreeQuery _octreeQuery; float _voxelSizeScale; int _boundaryLevelAdjust; diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index 97e7f67ff5..8913e88cf5 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -54,6 +54,7 @@ void OctreeScriptingInterface::init() { if (_initialized) { return; } + if (_jurisdictionListener) { _managedJurisdictionListener = false; } else { diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index ea897bbb4f..47b01c64f9 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -21,8 +21,7 @@ class OctreeScriptingInterface : public QObject { Q_OBJECT public: - OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, - JurisdictionListener* jurisdictionListener = NULL); + OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, JurisdictionListener* jurisdictionListener = NULL); ~OctreeScriptingInterface(); @@ -86,8 +85,8 @@ public slots: protected: /// attached OctreeEditPacketSender that handles queuing and sending of packets to VS - OctreeEditPacketSender* _packetSender; - JurisdictionListener* _jurisdictionListener; + OctreeEditPacketSender* _packetSender = nullptr; + JurisdictionListener* _jurisdictionListener = nullptr; bool _managedPacketSender; bool _managedJurisdictionListener; bool _initialized; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 0ec8b4ad24..ce387e648b 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -373,7 +373,10 @@ void DeferredLightingEffect::render(RenderArgs* args) { projMats[0] = monoProjMat; deferredTransforms[0].projection = monoProjMat; + deferredTransforms[0].viewInverse = monoViewMat; + viewTransforms[0] = monoViewTransform; + deferredTransforms[0].stereoSide = 0.0f; clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight); @@ -579,6 +582,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setResourceTexture(1, nullptr); batch.setResourceTexture(2, nullptr); batch.setResourceTexture(3, nullptr); + batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr); args->_context->render(batch); diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index e2c07c05d0..96e624c187 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -27,6 +27,10 @@ ScriptCache::ScriptCache(QObject* parent) { // nothing to do here... } +void ScriptCache::clearCache() { + _scriptCache.clear(); +} + QString ScriptCache::getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool reload) { QUrl url = ResourceManager::normalizeURL(unnormalizedURL); QString scriptContents; diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 7de14a09f7..da9f1a409c 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -28,6 +28,7 @@ class ScriptCache : public QObject, public Dependency { SINGLETON_DEPENDENCY public: + void clearCache(); void getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload = false); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a7136edd7b..692a320b4e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -263,7 +263,7 @@ void ScriptEngine::init() { } _isInitialized = true; - + auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); @@ -405,7 +405,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func QScriptValue setterFunction = newFunction(setter, 1); QScriptValue getterFunction = newFunction(getter); - if (!parent.isNull()) { + if (!parent.isNull() && !parent.isEmpty()) { QScriptValue object = globalObject().property(parent); if (object.isValid()) { object.setProperty(name, setterFunction, QScriptValue::PropertySetter); @@ -559,6 +559,7 @@ void ScriptEngine::run() { if (!_isInitialized) { init(); } + _isRunning = true; _isFinished = false; if (_wantSignals) { diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index f5adfe2bfd..d1f6166b3d 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -33,7 +33,7 @@ "children": [] }, { - "id": "controllerOverlay", + "id": "manipulatorOverlay", "type": "overlay", "data": { "alpha": 1.0, @@ -42,7 +42,7 @@ "children": [ { "id": "spineLean", - "type": "controller", + "type": "manipulator", "data": { "alpha": 1.0, "joints": [