From 339ecb1faa3897de1a9e0e5e1011fcb43ffdfdab Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 30 Oct 2018 16:54:34 -0700 Subject: [PATCH 01/60] adding the offsets to the AnimSkeleton.cpp --- libraries/animation/src/AnimSkeleton.cpp | 34 ++++++++++++++++++++++-- libraries/animation/src/AnimSkeleton.h | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bed9c590be..0a4303baf3 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -20,11 +20,30 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); + AnimPose identity; for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); + _avatarTPoseOffsets.push_back(identity); } buildSkeletonFromJoints(joints); -} + //add offsets for spine2 and the neck + _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); + _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); + + for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { + const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + // cast into a non-const reference, so we can mutate the FBXCluster + FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + // this works if clusters match joints one for one. + //cluster.inverseBindMatrix = (glm::mat4)_avatarTPoseOffsets[cluster.jointIndex].inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + + } + } +} AnimSkeleton::AnimSkeleton(const std::vector& joints) { buildSkeletonFromJoints(joints); @@ -189,8 +208,19 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); - _relativeDefaultPoses.push_back(relDefaultPose); + int parentIndex = getParentIndex(i); + + // putting the pipeline code is + // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. + // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. + //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; + //if (parentIndex >= 0) { + // relDefaultPose = _avatarTPoseOffsets[parentIndex].inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * _avatarTPoseOffsets[parentIndex] * AnimPose(relDefaultPose.rot(), glm::vec3()); + //} + + _relativeDefaultPoses.push_back(relDefaultPose); + if (parentIndex >= 0) { _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 2ebf3f4f5d..ad1120fcbf 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -67,6 +67,7 @@ protected: void buildSkeletonFromJoints(const std::vector& joints); std::vector _joints; + AnimPoseVec _avatarTPoseOffsets; int _jointsSize { 0 }; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; From e24f10a125e9a6ffba69b7c959b4ec665653e4c7 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 30 Oct 2018 17:33:28 -0700 Subject: [PATCH 02/60] debugging offset vector --- libraries/animation/src/AnimSkeleton.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 0a4303baf3..bd8d62bdb4 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -20,12 +20,14 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); - AnimPose identity; + _avatarTPoseOffsets.reserve(fbxGeometry.joints.size()); + AnimPose identity(glm::quat(), glm::vec3()); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); _avatarTPoseOffsets.push_back(identity); } buildSkeletonFromJoints(joints); + /* //add offsets for spine2 and the neck _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); @@ -43,6 +45,7 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { } } + */ } AnimSkeleton::AnimSkeleton(const std::vector& joints) { From c070bce2ec6cb257cc0f400fc7cea909b3663cd3 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 31 Oct 2018 08:39:35 -0700 Subject: [PATCH 03/60] Read joint rotation offsets from .fst --- libraries/animation/src/Rig.cpp | 13 ++++++++++ libraries/animation/src/Rig.h | 4 ++++ .../src/avatars-renderer/SkeletonModel.cpp | 3 ++- libraries/fbx/src/FSTReader.cpp | 24 +++++++++++++++++++ libraries/fbx/src/FSTReader.h | 3 +++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33b9569758..c58a96aa84 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2062,3 +2062,16 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } + +void Rig::setJointRotationOffsets(const QMap& offsets) { + _jointRotationOffsets.clear(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + glm::quat rotationOffset = itr.value(); + int jointIndex = indexOfJoint(jointName); + if (jointIndex != -1) { + _jointRotationOffsets.insert(jointIndex, rotationOffset); + } + qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + } +} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7a090bd7bd..9928810026 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -231,6 +231,8 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } + + void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -300,6 +302,8 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; + QMap _jointRotationOffsets; + glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1ec58fd704..816e078886 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Avatar.h" #include "Logging.h" @@ -82,7 +83,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - + _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 75596862d2..10ea9dc964 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,6 +207,30 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } +QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { + QMap jointRotationOffsets; + if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { + auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + QString line = itr.value().toString(); + auto eulerAngles = line.split(','); + if (eulerAngles.size() == 3) { + float eulerX = eulerAngles[0].mid(1).toFloat(); + float eulerY = eulerAngles[1].toFloat(); + float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); + if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + jointRotationOffsets.insert(jointName, rotationOffset); + } + } + } + } + return jointRotationOffsets; +} + QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 4a8574f0cf..ef649eb4d8 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,6 +14,7 @@ #include #include +#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; @@ -29,6 +30,7 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; +static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -51,6 +53,7 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); + static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); From 914491ae27e27ee0998a49967352bc3a776aa3c1 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 31 Oct 2018 11:48:55 -0700 Subject: [PATCH 04/60] fix YX coord swamp --- libraries/fbx/src/FSTReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 10ea9dc964..05690f5740 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -220,8 +220,8 @@ QMap FSTReader::getJointRotationOffsets(const QVariantHash& float eulerY = eulerAngles[1].toFloat(); float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); jointRotationOffsets.insert(jointName, rotationOffset); } From dcf20d2a15a674ec35b54e58e42eb1816b390689 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 26 Oct 2018 00:37:44 +0300 Subject: [PATCH 05/60] FB17381 - Reticle.setVisible isn't working --- scripts/system/controllers/controllerModules/mouseHMD.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 27fe82ca19..172923a8e2 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -29,6 +29,7 @@ function MouseHMD() { var _this = this; + this.hmdWasActive = HMD.active; this.mouseMoved = false; this.mouseActivity = new TimeLock(5000); this.handControllerActivity = new TimeLock(4000); @@ -102,6 +103,8 @@ this.isReady = function(controllerData, deltaTime) { var now = Date.now(); + var hmdChanged = this.hmdWasActive !== HMD.active; + this.hmdWasActive = HMD.active; this.triggersPressed(controllerData, now); if (HMD.active) { if (!this.mouseActivity.expired(now) && _this.handControllerActivity.expired()) { @@ -110,7 +113,7 @@ } else { Reticle.visible = false; } - } else if (!Reticle.visible) { + } else if (hmdChanged && !Reticle.visible) { Reticle.visible = true; } From 64d8fa6875ff402bc6a8a7bf8da62680e6d75b79 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 1 Nov 2018 13:33:03 -0700 Subject: [PATCH 06/60] added the const argument to the AnimSkeleton constructor that allows the offset rotations to be passed in --- .../src/avatars/ScriptableAvatar.cpp | 3 +- libraries/animation/src/AnimClip.cpp | 3 +- libraries/animation/src/AnimSkeleton.cpp | 43 +++++++++++-------- libraries/animation/src/AnimSkeleton.h | 6 +-- libraries/animation/src/Rig.cpp | 4 +- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 3 +- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 7d2b267a05..da151c7a35 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -84,7 +84,8 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getGeometry()); + QMap jointRotationOffsets; + _animSkeleton = std::make_shared(_bind->getGeometry(), jointRotationOffsets); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f9195a608b..ffbbf1b0f4 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -102,7 +102,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); - AnimSkeleton animSkeleton(geom); + QMap jointRotationOffsets; + AnimSkeleton animSkeleton(geom, jointRotationOffsets); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bd8d62bdb4..23e4b7a9d6 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,21 +16,20 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { +AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); - _avatarTPoseOffsets.reserve(fbxGeometry.joints.size()); - AnimPose identity(glm::quat(), glm::vec3()); + //_avatarTPoseOffsets.reserve(_jointsSize); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); - _avatarTPoseOffsets.push_back(identity); + //_avatarTPoseOffsets.push_back(AnimPose(glm::quat(), glm::vec3())); } - buildSkeletonFromJoints(joints); - /* - //add offsets for spine2 and the neck - _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); - _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); + + + // add offsets for spine2 and the neck + // _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); + // _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -40,16 +39,19 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - //cluster.inverseBindMatrix = (glm::mat4)_avatarTPoseOffsets[cluster.jointIndex].inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + if (jointOffsets.contains(j)) { + AnimPose localOffset(jointOffsets[j], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } } } - */ + buildSkeletonFromJoints(joints, jointOffsets); } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { - buildSkeletonFromJoints(joints); +AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { + buildSkeletonFromJoints(joints, jointOffsets); } int AnimSkeleton::nameToJointIndex(const QString& jointName) const { @@ -188,7 +190,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses @@ -218,9 +220,14 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - //if (parentIndex >= 0) { - // relDefaultPose = _avatarTPoseOffsets[parentIndex].inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * _avatarTPoseOffsets[parentIndex] * AnimPose(relDefaultPose.rot(), glm::vec3()); - //} + if (jointOffsets.contains(i)) { + AnimPose localOffset(jointOffsets[i], glm::vec3()); + relDefaultPose = relDefaultPose * localOffset; + if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { + AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } + } _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ad1120fcbf..0a5b177b63 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,8 +23,8 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const FBXGeometry& fbxGeometry); - explicit AnimSkeleton(const std::vector& joints); + explicit AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets); + explicit AnimSkeleton(const std::vector& joints, const QMap jointOffsets); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -64,7 +64,7 @@ public: std::vector lookUpJointIndices(const std::vector& jointNames) const; protected: - void buildSkeletonFromJoints(const std::vector& joints); + void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); std::vector _joints; AnimPoseVec _avatarTPoseOffsets; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c58a96aa84..c7e5226c7b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(geometry,_jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -310,7 +310,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff void Rig::reset(const FBXGeometry& geometry) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index e9d8243e38..cb623fd53e 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -55,7 +55,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc } QByteArray blob = file.readAll(); std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry)); + QMap jointRotationOffsets; + std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry, jointRotationOffsets)); skeleton->dump(verbose); } From 5392d21b1c9def68a0de2f52029276e8239b3b1b Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 2 Nov 2018 17:59:18 -0700 Subject: [PATCH 07/60] loading test file to check rotation offsets --- libraries/animation/src/AnimSkeleton.cpp | 7 +++++++ libraries/animation/src/Rig.cpp | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 23e4b7a9d6..ab914b1da3 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -17,6 +17,8 @@ #include "AnimationLogging.h" AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { + + qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; joints.reserve(fbxGeometry.joints.size()); @@ -34,12 +36,17 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); + if ((cluster.jointIndex == nameToJointIndex("Neck")) || (cluster.jointIndex == nameToJointIndex("Spine2"))) { + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[j]; + } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. if (jointOffsets.contains(j)) { + qCDebug(animation) << "found a joint offset to add " << j << " " << jointOffsets[j]; AnimPose localOffset(jointOffsets[j], glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c7e5226c7b..f1cb94a99c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2074,4 +2074,13 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { } qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } + int neckIndex = indexOfJoint("Neck"); + int spine2Index = indexOfJoint("Spine2"); + if (neckIndex != -1) { + _jointRotationOffsets.insert(neckIndex, glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f)); + } + if (spine2Index != -1) { + _jointRotationOffsets.insert(spine2Index, glm::quat(0.7071f, -0.7071f, 0.0f, 0.0f)); + } + qCDebug(animation) << "set the neck and spine2 offsets"; } \ No newline at end of file From d231a35dfacfccbdcf25cb8050685c7df6064ee6 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 08:35:01 -0800 Subject: [PATCH 08/60] changes to get spine2 and neck to work almost there --- libraries/animation/src/AnimSkeleton.cpp | 38 +++++++++++++++---- libraries/animation/src/Rig.cpp | 14 +++---- .../src/avatars-renderer/SkeletonModel.cpp | 3 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index ab914b1da3..cc4a66027d 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -37,17 +37,30 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if ((cluster.jointIndex == nameToJointIndex("Neck")) || (cluster.jointIndex == nameToJointIndex("Spine2"))) { - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[j]; + if (jointOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; + } + + if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { + qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - if (jointOffsets.contains(j)) { - qCDebug(animation) << "found a joint offset to add " << j << " " << jointOffsets[j]; - AnimPose localOffset(jointOffsets[j], glm::vec3()); + if (cluster.jointIndex == 62) { + qCDebug(animation) << "Neck"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + if (cluster.jointIndex == 13) { + qCDebug(animation) << "Spine2"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -227,14 +240,25 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; + + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { + //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + // AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + // relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); } } + if ((parentIndex == 13) && jointOffsets.contains(13)) { + AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } + if ((parentIndex == 62) && jointOffsets.contains(62)) { + AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + } _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index f1cb94a99c..ff9d19c8a2 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2074,13 +2074,13 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { } qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - int neckIndex = indexOfJoint("Neck"); - int spine2Index = indexOfJoint("Spine2"); - if (neckIndex != -1) { - _jointRotationOffsets.insert(neckIndex, glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f)); + int neckId = 62; + int spine2Id = 13; + if (true){ //neckIndex != -1) { + _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f)); } - if (spine2Index != -1) { - _jointRotationOffsets.insert(spine2Index, glm::quat(0.7071f, -0.7071f, 0.0f, 0.0f)); + if (true){ //spine2Index != -1) { + _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } - qCDebug(animation) << "set the neck and spine2 offsets"; + qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; } \ No newline at end of file diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 816e078886..c6123ef003 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -57,6 +57,7 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { void SkeletonModel::initJointStates() { const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); + _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); _rig.initJointStates(geometry, modelOffset); { @@ -83,7 +84,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); + //_rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); From bcde01621d13417baab6a2a173c8977a5b1aef6d Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 5 Nov 2018 14:25:52 -0700 Subject: [PATCH 09/60] set offsets on FBXGeometry --- libraries/animation/src/Rig.cpp | 13 ------- libraries/animation/src/Rig.h | 4 -- .../src/avatars-renderer/SkeletonModel.cpp | 1 - libraries/fbx/src/FBX.h | 2 + libraries/fbx/src/FBXReader.cpp | 38 +++++++++++++++++++ libraries/fbx/src/FSTReader.cpp | 24 ------------ libraries/fbx/src/FSTReader.h | 2 - 7 files changed, 40 insertions(+), 44 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index c58a96aa84..33b9569758 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2062,16 +2062,3 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } - -void Rig::setJointRotationOffsets(const QMap& offsets) { - _jointRotationOffsets.clear(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - glm::quat rotationOffset = itr.value(); - int jointIndex = indexOfJoint(jointName); - if (jointIndex != -1) { - _jointRotationOffsets.insert(jointIndex, rotationOffset); - } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; - } -} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9928810026..7a090bd7bd 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -231,8 +231,6 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - - void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -302,8 +300,6 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - QMap _jointRotationOffsets; - glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 816e078886..bf6af763a3 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -83,7 +83,6 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fdebb16bc8..ec3a29e7f5 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -366,6 +366,8 @@ public: QString getModelNameOfMesh(int meshIndex) const; QList blendshapeChannelNames; + + QMap jointRotationOffsets; }; Q_DECLARE_METATYPE(FBXGeometry) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index dd766f002c..44976adfa8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -615,6 +615,31 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } +QMap getJointRotationOffsets(const QVariantHash& mapping) { + QMap jointRotationOffsets; + static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; + if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { + auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + QString line = itr.value().toString(); + auto eulerAngles = line.split(','); + if (eulerAngles.size() == 3) { + float eulerX = eulerAngles[0].mid(1).toFloat(); + float eulerY = eulerAngles[1].toFloat(); + float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); + if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { + glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + jointRotationOffsets.insert(jointName, rotationOffset); + } + } + } + } + return jointRotationOffsets; +} + FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; @@ -1991,6 +2016,19 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } + + auto offsets = getJointRotationOffsets(mapping); + geometry.jointRotationOffsets.clear(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + glm::quat rotationOffset = itr.value(); + int jointIndex = geometry.getJointIndex(jointName); + if (jointIndex != -1) { + geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); + } + qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + } + return geometryPtr; } diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 05690f5740..75596862d2 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,30 +207,6 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } -QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { - QMap jointRotationOffsets; - if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { - auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); - jointRotationOffsets.insert(jointName, rotationOffset); - } - } - } - } - return jointRotationOffsets; -} - QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index ef649eb4d8..3729cbaf16 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -30,7 +30,6 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; -static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -53,7 +52,6 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); - static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); From 8310dd0cbac7203e15e39af8888b3dcc3f03fb0c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 5 Nov 2018 15:10:09 -0700 Subject: [PATCH 10/60] Delete leftovers --- .../avatars-renderer/src/avatars-renderer/SkeletonModel.cpp | 2 +- libraries/fbx/src/FSTReader.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index bf6af763a3..fce33d38c1 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "Avatar.h" #include "Logging.h" @@ -83,6 +82,7 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; + computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 3729cbaf16..4a8574f0cf 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,7 +14,6 @@ #include #include -#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; From 484eb8ba09f2c42035623f012f5c1051f97a56d2 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 14:56:58 -0800 Subject: [PATCH 11/60] debugging unity offsets --- libraries/animation/src/AnimSkeleton.cpp | 32 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index cc4a66027d..39ab5a178e 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -28,11 +28,11 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap& joints, const QMap jointOffsets) { @@ -240,7 +248,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - + /* QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); @@ -252,13 +260,23 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } if ((parentIndex == 13) && jointOffsets.contains(13)) { + if (i == 62) { + qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); + } AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + if (i == 62) { + qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); + } } if ((parentIndex == 62) && jointOffsets.contains(62)) { - AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); + //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); + //relDefaultPose = relDefaultPose * localOffset; + //AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); } + */ _relativeDefaultPoses.push_back(relDefaultPose); From f0c02bb49bbdb44b04f4c27226deb56147280829 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 5 Nov 2018 17:36:54 -0800 Subject: [PATCH 12/60] more debug --- libraries/animation/src/AnimSkeleton.cpp | 25 ++++++++++++------------ libraries/animation/src/Rig.cpp | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 39ab5a178e..49ffe75ba0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -32,7 +32,7 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if (jointOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; - } if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; @@ -51,8 +48,8 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap& joints, // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); + qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); @@ -248,7 +246,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - /* + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); @@ -264,19 +262,20 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); } AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); if (i == 62) { qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); } } if ((parentIndex == 62) && jointOffsets.contains(62)) { - qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); + //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); //relDefaultPose = relDefaultPose * localOffset; - //AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - //relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); + relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); } - */ + _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ff9d19c8a2..3302760f6b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2077,7 +2077,7 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int neckId = 62; int spine2Id = 13; if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f)); + _jointRotationOffsets.insert(neckId, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From 599b7761b2a7d44b848215a0bb001ac4d21e08bd Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 6 Nov 2018 11:09:56 -0700 Subject: [PATCH 13/60] Read quats from fst --- libraries/fbx/src/FBXReader.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 44976adfa8..c8dc78bd91 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -623,15 +623,14 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { QString jointName = itr.key(); QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); + auto quatCoords = line.split(','); + if (quatCoords.size() == 4) { + float quatX = quatCoords[0].mid(1).toFloat(); + float quatY = quatCoords[1].toFloat(); + float quatZ = quatCoords[2].toFloat(); + float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat(); + if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { + glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); } } @@ -2026,7 +2025,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (jointIndex != -1) { geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); } - qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } return geometryPtr; From e0d1ef3be3128a9433787bb76bc27881c5f4d07f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 6 Nov 2018 11:11:16 -0700 Subject: [PATCH 14/60] Fix debug output --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index c8dc78bd91..c357d0f430 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2025,7 +2025,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (jointIndex != -1) { geometry.jointRotationOffsets.insert(jointIndex, rotationOffset); } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } return geometryPtr; From e52f2ab7d6210ca8be1fdfe65dfb2aa74b5d9128 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 14:14:57 -0800 Subject: [PATCH 15/60] works for two joints now --- libraries/animation/src/AnimSkeleton.cpp | 21 +-------------------- libraries/animation/src/Rig.cpp | 3 ++- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 49ffe75ba0..2dfcdee4b0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -252,30 +252,11 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; - if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - // AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - // relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - } } - if ((parentIndex == 13) && jointOffsets.contains(13)) { - if (i == 62) { - qCDebug(animation) << "the neck translation is " << relDefaultPose.trans(); - } + if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - if (i == 62) { - qCDebug(animation) << "the neck translation redo is " << relDefaultPose.trans(); - } } - if ((parentIndex == 62) && jointOffsets.contains(62)) { - - //AnimPose localOffset(glm::quat(0.7071f, 0.0f, 0.7071f, 0.0f), glm::vec3()); - //relDefaultPose = relDefaultPose * localOffset; - AnimPose localParentOffset(glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f), glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); - qCDebug(animation) << "the head translation is " << relDefaultPose.trans(); - } - _relativeDefaultPoses.push_back(relDefaultPose); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3302760f6b..02648d5771 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2077,7 +2077,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int neckId = 62; int spine2Id = 13; if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) + qCDebug(animation) << "multiplied quats are " << glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From 1dbccc814b9471a554573d31ce85332b586039b3 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 17:35:53 -0800 Subject: [PATCH 16/60] working on the ik dislocation caused by the offsets, head is what I am working on --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MySkeletonModel.cpp | 8 ++++++-- libraries/animation/src/AnimSkeleton.cpp | 15 +++------------ libraries/animation/src/Rig.cpp | 6 +++++- libraries/animation/src/Rig.h | 2 ++ 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3299bd10e7..0f9d3f2f81 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - _skeletonModel->reset(); + //_skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index c6aae6124a..858cba69dd 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -91,6 +91,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); + const QMap jointOffsetMap = _rig.getJointRotationOffsets(); Head* head = _owningAvatar->getHead(); @@ -118,8 +119,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { + qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; + AnimPose jointOffset(jointOffsetMap[62], glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffset.inverse() * (avatarToRigPose * pose) * jointOffset; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and @@ -229,7 +232,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; + if (false && myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 2dfcdee4b0..05953e2803 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -41,19 +41,10 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap(mesh.clusters.at(j)); - if ((cluster.jointIndex == 62) || (cluster.jointIndex == 13)) { - qCDebug(animation) << "cluster joint equals index " << cluster.jointIndex; - } // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. - if (cluster.jointIndex == 63) { - //qCDebug(animation) << "Head"; - //qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - //AnimPose localOffset(glm::quat(.7071f, 0.0f, .7071f, 0.0f), glm::vec3()); - //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } + if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; @@ -246,11 +237,11 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - + QString jointName = getJointName(i); if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); - AnimPose localOffset(jointOffsets[i], glm::vec3()); + AnimPose localOffset(jointOffsets[i], glm::vec3()); relDefaultPose = relDefaultPose * localOffset; } if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 02648d5771..1e20ec9a7d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry,_jointRotationOffsets); + _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -2084,4 +2084,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; +} + +const QMap& Rig::getJointRotationOffsets() const { + return _jointRotationOffsets; } \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9928810026..dd7e766a49 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -128,6 +128,7 @@ public: int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; QString nameOfJoint(int jointIndex) const; + const QMap& getJointRotationOffsets() const; void setModelOffset(const glm::mat4& modelOffsetMat); @@ -422,6 +423,7 @@ protected: SnapshotBlendPoseHelper _hipsBlendHelper; ControllerParameters _previousControllerParameters; + bool _alreadyInitialized { false }; }; #endif /* defined(__hifi__Rig__) */ From dfd0ce69cbade0f1ead879c597ce17f451452799 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 6 Nov 2018 17:36:05 -0800 Subject: [PATCH 17/60] more head ik --- interface/src/avatar/MySkeletonModel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 858cba69dd..6aa4d1402f 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -120,9 +120,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; - AnimPose jointOffset(jointOffsetMap[62], glm::vec3()); + AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); + AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffset.inverse() * (avatarToRigPose * pose) * jointOffset; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffsetSpine2.inverse() * jointOffsetNeck.inverse() * (avatarToRigPose * pose) * jointOffsetNeck * jointOffsetSpine2; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and From 6b4620b4a14d5254925bd5e54ada3caf61d54edb Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 7 Nov 2018 17:32:23 -0800 Subject: [PATCH 18/60] working on the input mapper problem for the offset rotations --- interface/src/avatar/MyAvatar.cpp | 5 ++++- interface/src/avatar/MySkeletonModel.cpp | 15 +++++++++++++-- libraries/animation/src/Rig.cpp | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0f9d3f2f81..39e6a56b02 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - //_skeletonModel->reset(); + _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); @@ -2037,7 +2037,9 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); + qCDebug(interfaceapp) << "avatar sensor orientation " << pose.getRotation(); if (pose.valid) { + qCDebug(interfaceapp) << "sensor to world matrix orientation " << extractRotation(getSensorToWorldMatrix()); return pose.transform(getSensorToWorldMatrix()); } else { return controller::Pose(); // invalid pose @@ -2047,6 +2049,7 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); if (pose.valid) { + qCDebug(interfaceapp) << "avatar world orientation " << getWorldOrientation(); glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); } else { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6aa4d1402f..d389d99e26 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -119,11 +119,22 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { + AnimPose previousHeadPose; + bool headUnfuckedWith = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), previousHeadPose); + if (headUnfuckedWith) { + qCDebug(interfaceapp) << "unset head position " << previousHeadPose.trans(); + qCDebug(interfaceapp) << "unset head rotation " << previousHeadPose.rot(); + } qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; + qCDebug(interfaceapp) << "head joint avatar frame " << avatarHeadPose.getRotation(); AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); + AnimPose testPose(glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f), glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = jointOffsetSpine2.inverse() * jointOffsetNeck.inverse() * (avatarToRigPose * pose) * jointOffsetNeck * jointOffsetSpine2; + AnimPose newHeadRot = (avatarToRigPose * pose) * testPose; + AnimPose newHeadRot2(newHeadRot.rot(), avatarHeadPose.getTranslation()); + AnimPose identityPose(glm::quat(1.0f,0.0f,0.0f,0.0f), glm::vec3(0.0f,0.57f,0.0f)); + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and @@ -234,7 +245,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // set spine2 if we have hand controllers qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; - if (false && myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1e20ec9a7d..a73c945124 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2078,7 +2078,7 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { int spine2Id = 13; if (true){ //neckIndex != -1) { _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) - qCDebug(animation) << "multiplied quats are " << glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); + qCDebug(animation) << "multiplied quats are " << glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); } if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); From 298c5efe69a1d347b84481f86d83dbf7509f8682 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 12:13:56 -0800 Subject: [PATCH 19/60] Simple demo of uploading file in chunks --- .../resources/web/content/js/content.js | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 346e846748..61fee425ff 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,6 +14,81 @@ $(document).ready(function(){ return html; } + function uploadInChunks(file) { + var fileSize = file.size; + var filename = file.name; + var CHUNK_SIZE = 16384; + + for(p = 0; p <= fileSize; p += CHUNK_SIZE) { + var chunk = file.slice(p, p + CHUNK_SIZE, file.type); + var chunkFormData = new FormData(); + chunkFormData.append('restore-file-chunk', chunk, filename); + var ajaxParams = { + url: '/content/upload', + type: 'POST', + async: false, + timeout: 60, + processData: false, + contentType: false, + data: chunkFormData + }; + var ajaxObject = $.ajax(ajaxParams); + + } + + + } + + function uploadNextChunk(file, offset) + { + var fileSize = file.size; + var filename = file.name; + + var CHUNK_SIZE = 16384; + if (offset == undefined) { + offset = 0; + } + var isFinal = fileSize - offset > CHUNK_SIZE ? false : true; + var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); + var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); + var chunkFormData = new FormData(); + + var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; + chunkFormData.append(formItemName, chunk, filename); + var ajaxParams = { + url: '/content/upload', + type: 'POST', + timeout: 30000, + cache: false, + processData: false, + data: chunkFormData + }; + + var ajaxObject = $.ajax(ajaxParams); + ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { + showErrorMessage( + "Error", + "There was a problem restoring domain content.\n" + + "Please ensure that the content archive or entity file is valid and try again." + ); + }); + + if (!isFinal) { + ajaxObject.done(function (data, textStatus, jqXHR) + {uploadNextChunk(file, offset + CHUNK_SIZE);}); + } else { + ajaxObject.done(function(data, textStatus, jqXHR) { + isRestoring = true; + + // immediately reload backup information since one should be restoring now + reloadBackupInformation(); + + swal.close(); + }); + } + + } + function setupBackupUpload() { // construct the HTML needed for the settings backup panel var html = "
"; @@ -56,6 +131,11 @@ $(document).ready(function(){ showSpinnerAlert("Uploading content to restore"); + + uploadNextChunk(files[0]); + return; + + // Previous one-upload method. $.ajax({ url: '/content/upload', type: 'POST', From bb60324335d4c733e3d8cd26bd0519b458b45731 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 14:01:27 -0800 Subject: [PATCH 20/60] Chunked content upload - working proof-of-concept --- .../resources/web/content/js/content.js | 5 +- domain-server/src/DomainServer.cpp | 76 ++++++++++++------- domain-server/src/DomainServer.h | 2 + 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 61fee425ff..d581ddaf85 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -44,7 +44,7 @@ $(document).ready(function(){ var fileSize = file.size; var filename = file.name; - var CHUNK_SIZE = 16384; + var CHUNK_SIZE = 65536; if (offset == undefined) { offset = 0; } @@ -58,9 +58,10 @@ $(document).ready(function(){ var ajaxParams = { url: '/content/upload', type: 'POST', - timeout: 30000, + timeout: 30000, // 30 s cache: false, processData: false, + contentType: false, data: chunkFormData }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b540592d7e..fd378cb39b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2258,41 +2258,63 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // check the file extension to see what kind of file this is // to make sure we handle this filetype for a content restore auto dispositionValue = QString(firstFormData.first.value("Content-Disposition")); - auto formDataFilenameRegex = QRegExp("filename=\"(.+)\""); - auto matchIndex = formDataFilenameRegex.indexIn(dispositionValue); + QRegExp formDataFieldsRegex(R":(name="(restore-file.*)".*filename="(.+)"):"); + auto matchIndex = formDataFieldsRegex.indexIn(dispositionValue); + QString formItemName = ""; QString uploadedFilename = ""; if (matchIndex != -1) { - uploadedFilename = formDataFilenameRegex.cap(1); + formItemName = formDataFieldsRegex.cap(1); + uploadedFilename = formDataFieldsRegex.cap(2); } - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - - // respond with a 200 for success + _pendingUploadedContent += firstFormData.second; + if (formItemName == "restore-file-chunk") { + // Received another chunk connection->respond(HTTPConnection::StatusCode200); - } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); + } else if (formItemName == "restore-file-chunk-final") { + if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) + || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); + _pendingUploadedContent.clear(); + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else if (formItemName == "restore-file") { + if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) + || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { - if (!connectionPtr) { - return; + _pendingUploadedContent.clear(); + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { + auto deferred = makePromise("recoverFromUploadedBackup"); + + deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { + if (!connectionPtr) { + return; + } + + QJsonObject rootJSON; + auto success = result["success"].toBool(); + rootJSON["success"] = success; + QJsonDocument docJSON(rootJSON); + connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), + JSON_MIME_TYPE.toUtf8()); + }); + + _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); + _pendingUploadedContent.clear(); + + return true; } - - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); - }); - - _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); - - return true; + } else { + connection->respond(HTTPConnection::StatusCode400); + } } else { // we don't have handling for this filetype, send back a 400 for failure connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e2bddc1aa5..db50af546e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -281,6 +281,8 @@ private: QHash> _pendingOAuthConnections; + QByteArray _pendingUploadedContent; + QThread _assetClientThread; }; From a21d10ad1b086ae932f1db2a3b05f85306b3daa7 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Nov 2018 16:34:02 -0800 Subject: [PATCH 21/60] Restore content archives (zip) correctly; other tweaks --- .../resources/web/content/js/content.js | 63 ++------------- domain-server/src/DomainServer.cpp | 78 +++++++++---------- domain-server/src/DomainServer.h | 2 + 3 files changed, 43 insertions(+), 100 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index d581ddaf85..38f93a57be 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,41 +14,18 @@ $(document).ready(function(){ return html; } - function uploadInChunks(file) { - var fileSize = file.size; - var filename = file.name; - var CHUNK_SIZE = 16384; - - for(p = 0; p <= fileSize; p += CHUNK_SIZE) { - var chunk = file.slice(p, p + CHUNK_SIZE, file.type); - var chunkFormData = new FormData(); - chunkFormData.append('restore-file-chunk', chunk, filename); - var ajaxParams = { - url: '/content/upload', - type: 'POST', - async: false, - timeout: 60, - processData: false, - contentType: false, - data: chunkFormData - }; - var ajaxObject = $.ajax(ajaxParams); - - } - - - } - function uploadNextChunk(file, offset) { + if (offset == undefined) { + offset = 0; + } + var fileSize = file.size; var filename = file.name; var CHUNK_SIZE = 65536; - if (offset == undefined) { - offset = 0; - } - var isFinal = fileSize - offset > CHUNK_SIZE ? false : true; + + var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); var chunkFormData = new FormData(); @@ -127,38 +104,10 @@ $(document).ready(function(){ function() { var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files'); - var fileFormData = new FormData(); - fileFormData.append('restore-file', files[0]); - showSpinnerAlert("Uploading content to restore"); uploadNextChunk(files[0]); - return; - - // Previous one-upload method. - $.ajax({ - url: '/content/upload', - type: 'POST', - timeout: 3600000, // Set timeout to 1h - cache: false, - processData: false, - contentType: false, - data: fileFormData - }).done(function(data, textStatus, jqXHR) { - isRestoring = true; - - // immediately reload backup information since one should be restoring now - reloadBackupInformation(); - - swal.close(); - }).fail(function(jqXHR, textStatus, errorThrown) { - showErrorMessage( - "Error", - "There was a problem restoring domain content.\n" - + "Please ensure that the content archive or entity file is valid and try again." - ); - }); } ); }); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index fd378cb39b..117313462b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2273,53 +2273,12 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // Received another chunk connection->respond(HTTPConnection::StatusCode200); } else if (formItemName == "restore-file-chunk-final") { - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContent.clear(); - // respond with a 200 for success - connection->respond(HTTPConnection::StatusCode200); + readPendingContent(connection, uploadedFilename); } else if (formItemName == "restore-file") { - if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive) - || uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second)); - - _pendingUploadedContent.clear(); - // respond with a 200 for success - connection->respond(HTTPConnection::StatusCode200); - } else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); - - deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } - - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); - }); - - _contentManager->recoverFromUploadedBackup(deferred, firstFormData.second); - _pendingUploadedContent.clear(); - - return true; - } - } else { - connection->respond(HTTPConnection::StatusCode400); - } + readPendingContent(connection, uploadedFilename); } else { - // we don't have handling for this filetype, send back a 400 for failure connection->respond(HTTPConnection::StatusCode400); } - } else { // respond with a 400 for failure connection->respond(HTTPConnection::StatusCode400); @@ -2568,6 +2527,39 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } +void DomainServer::readPendingContent(HTTPConnection* connection, QString filename) { + if (filename.endsWith(".json", Qt::CaseInsensitive) + || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); + + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + _pendingUploadedContent.clear(); + } else if (filename.endsWith(".zip", Qt::CaseInsensitive)) { + auto deferred = makePromise("recoverFromUploadedBackup"); + + QPointer connectionPtr(connection); + const QString JSON_MIME_TYPE = "application/json"; + deferred->then([connectionPtr, JSON_MIME_TYPE, this](QString error, QVariantMap result) { + if (!connectionPtr) { + return; + } + + QJsonObject rootJSON; + auto success = result["success"].toBool(); + rootJSON["success"] = success; + QJsonDocument docJSON(rootJSON); + connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), + JSON_MIME_TYPE.toUtf8()); + _pendingUploadedContent.clear(); + }); + + _contentManager->recoverFromUploadedBackup(deferred, _pendingUploadedContent); + } +} + HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) { // grab the UUID state property from the reply QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index db50af546e..b2ef933bc3 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -209,6 +209,8 @@ private: HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply); + void readPendingContent(HTTPConnection* connection, QString filename); + bool forwardMetaverseAPIRequest(HTTPConnection* connection, const QString& metaversePath, const QString& requestSubobject, From 8735b409ab110e4cc7b9d67b18932a6f37711782 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 8 Nov 2018 17:12:10 -0800 Subject: [PATCH 22/60] in the process of changing the rotation application to being absolute joints not local --- interface/src/avatar/MyAvatar.cpp | 3 - interface/src/avatar/MySkeletonModel.cpp | 17 +---- libraries/animation/src/AnimSkeleton.cpp | 82 +++++++++++++----------- libraries/animation/src/Rig.cpp | 2 + 4 files changed, 48 insertions(+), 56 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 39e6a56b02..3299bd10e7 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2037,9 +2037,7 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); - qCDebug(interfaceapp) << "avatar sensor orientation " << pose.getRotation(); if (pose.valid) { - qCDebug(interfaceapp) << "sensor to world matrix orientation " << extractRotation(getSensorToWorldMatrix()); return pose.transform(getSensorToWorldMatrix()); } else { return controller::Pose(); // invalid pose @@ -2049,7 +2047,6 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); if (pose.valid) { - qCDebug(interfaceapp) << "avatar world orientation " << getWorldOrientation(); glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); } else { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index d389d99e26..6026794cda 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -119,21 +119,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { - AnimPose previousHeadPose; - bool headUnfuckedWith = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), previousHeadPose); - if (headUnfuckedWith) { - qCDebug(interfaceapp) << "unset head position " << previousHeadPose.trans(); - qCDebug(interfaceapp) << "unset head rotation " << previousHeadPose.rot(); - } - qCDebug(interfaceapp) << "neck joint offset " << jointOffsetMap[62]; - qCDebug(interfaceapp) << "head joint avatar frame " << avatarHeadPose.getRotation(); - AnimPose jointOffsetNeck(jointOffsetMap[62], glm::vec3()); - AnimPose jointOffsetSpine2(jointOffsetMap[13], glm::vec3()); - AnimPose testPose(glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f), glm::vec3()); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - AnimPose newHeadRot = (avatarToRigPose * pose) * testPose; - AnimPose newHeadRot2(newHeadRot.rot(), avatarHeadPose.getTranslation()); - AnimPose identityPose(glm::quat(1.0f,0.0f,0.0f,0.0f), glm::vec3(0.0f,0.57f,0.0f)); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { @@ -244,7 +230,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; + //qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { @@ -255,6 +241,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + if (spine2Exists && headExists && hipsExists) { AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 05953e2803..d2bed40a06 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -15,6 +15,7 @@ #include #include "AnimationLogging.h" +static bool notBound = true; AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap jointOffsets) { @@ -22,48 +23,46 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry, const QMap joints; joints.reserve(fbxGeometry.joints.size()); - //_avatarTPoseOffsets.reserve(_jointsSize); for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); - //_avatarTPoseOffsets.push_back(AnimPose(glm::quat(), glm::vec3())); } buildSkeletonFromJoints(joints, jointOffsets); // add offsets for spine2 and the neck - // _avatarTPoseOffsets[nameToJointIndex("Spine2")] = AnimPose(glm::quat(-0.707107f, 0.0f, 0.0f, 0.707107f), glm::vec3()); - // _avatarTPoseOffsets[nameToJointIndex("Neck")] = AnimPose(glm::quat(0.0f, 0.707107f, 0.0f, 0.707107f), glm::vec3()); - - for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { - - - // cast into a non-const reference, so we can mutate the FBXCluster - FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + if (notBound) { + notBound = false; + for (int i = 0; i < (int)fbxGeometry.meshes.size(); i++) { + const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. - // this works if clusters match joints one for one. - - if (cluster.jointIndex == 62) { - qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - if (cluster.jointIndex == 13) { - qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } + // cast into a non-const reference, so we can mutate the FBXCluster + FBXCluster& cluster = const_cast(mesh.clusters.at(j)); + + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + // this works if clusters match joints one for one. + + if (cluster.jointIndex == 62) { + qCDebug(animation) << "Neck"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + if (cluster.jointIndex == 13) { + qCDebug(animation) << "Spine2"; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; + AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + + } } + } - } AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { @@ -232,6 +231,12 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); + AnimPose newAbsPose; + if (parentIndex >= 0) { + newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); + } else { + newAbsPose = relDefaultPose; + } // putting the pipeline code is // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. @@ -242,20 +247,21 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, if (jointOffsets.contains(i)) { //QString parentIndex = getJointName(parentIndex); AnimPose localOffset(jointOffsets[i], glm::vec3()); - relDefaultPose = relDefaultPose * localOffset; + newAbsPose = newAbsPose * localOffset; } if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - relDefaultPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(relDefaultPose.rot(), glm::vec3()); + newAbsPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(newAbsPose.rot(), glm::vec3()); } - _relativeDefaultPoses.push_back(relDefaultPose); - if (parentIndex >= 0) { - _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); - } else { - _absoluteDefaultPoses.push_back(relDefaultPose); + relDefaultPose = _absoluteDefaultPoses[parentIndex].inverse() * newAbsPose; } + _relativeDefaultPoses.push_back(relDefaultPose); + + + _absoluteDefaultPoses.push_back(newAbsPose); + } for (int i = 0; i < _jointsSize; i++) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a73c945124..bdbbd02706 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2084,6 +2084,8 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; + glm::quat testRot(glm::vec3(0.000018095f, -4.74360667058f, -89.9994155926f)); + qCDebug(animation) << "test rot from euler" << testRot; } const QMap& Rig::getJointRotationOffsets() const { From 244b768b9ae279e978e14768bb8963e60dc1513d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 8 Nov 2018 23:42:50 -0800 Subject: [PATCH 23/60] fixed the absolute default joint poses so they work for the 2 joint avatar --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 38 ++++++++-- libraries/animation/src/Rig.cpp | 79 ++++++++++++++++++--- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 11 ++- 4 files changed, 109 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8d47591d40..502be9d57c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - _skeletonModel->reset(); + //_skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 03eac7de49..af0ec0d5b0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -28,8 +28,8 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap buildSkeletonFromJoints(joints, jointOffsets); // add offsets for spine2 and the neck - if (notBound) { - notBound = false; + + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -59,7 +59,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap } } - } + } @@ -230,6 +230,34 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); + + AnimPose newAbsPose; + if (parentIndex >= 0) { + newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; + + _absoluteDefaultPoses.push_back(newAbsPose); + } else { + + _absoluteDefaultPoses.push_back(relDefaultPose); + } + + } + for (int k = 0; k < _jointsSize; k++) { + int parentIndex2 = getParentIndex(k); + if (jointOffsets.contains(k)) { + AnimPose localOffset(jointOffsets[k], glm::vec3()); + _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; + } + if (parentIndex2 >= 0) { + + _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); + } else { + + _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); + } + } + + /* AnimPose newAbsPose; if (parentIndex >= 0) { newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); @@ -260,9 +288,9 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { _absoluteDefaultPoses.push_back(newAbsPose); - + } - + */ for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6f1e37d9ff..28ef0aef5c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -310,11 +310,9 @@ void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset void Rig::reset(const HFMModel& hfmModel) { _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); -<<<<<<< HEAD - _animSkeleton = std::make_shared(geometry, _jointRotationOffsets); -======= - _animSkeleton = std::make_shared(hfmModel); ->>>>>>> upstream/master + + _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -2087,9 +2085,74 @@ void Rig::setJointRotationOffsets(const QMap& offsets) { if (true){ //spine2Index != -1) { _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); } - qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; - glm::quat testRot(glm::vec3(0.000018095f, -4.74360667058f, -89.9994155926f)); - qCDebug(animation) << "test rot from euler" << testRot; + + std::vector eulersFromEngineer; + eulersFromEngineer.push_back(glm::vec3(-81.1050620752f, -20.1443891394f, 100.309499473f)); + eulersFromEngineer.push_back(glm::vec3(260.889773298f, -20.143735467f, 79.7184972987f)); + eulersFromEngineer.push_back(glm::vec3(-196.885439853f, -15.4618394328f, 107.273377257f)); + eulersFromEngineer.push_back(glm::vec3(185.169288353f, -14.3865564204f, 73.5514142013f)); + eulersFromEngineer.push_back(glm::vec3(85.0436206363f, -0.405424354654f, 279.126747746f)); + eulersFromEngineer.push_back(glm::vec3(265.040827938f, -24.8809133357f, 80.8874530433f)); + eulersFromEngineer.push_back(glm::vec3(0.0000012746f, -9.5408567955f, 89.9999827841f)); + eulersFromEngineer.push_back(glm::vec3(-304.676178613f, -1.77806457464f, 94.6267844984f)); + eulersFromEngineer.push_back(glm::vec3(299.239708621f, -1.75410019523f, 85.3712190608f)); + eulersFromEngineer.push_back(glm::vec3(-70.5442845855f, 180.00795643f, -73.899282669f)); + eulersFromEngineer.push_back(glm::vec3(-5.65981473995f, -0.554837824572f, 90.3818083467f)); + eulersFromEngineer.push_back(glm::vec3(282.516400893f, -2.86457680169f, 106.966109268f)); + eulersFromEngineer.push_back(glm::vec3(-5.74446362761f, -3.69252247483f, 89.4088276443f)); + eulersFromEngineer.push_back(glm::vec3(175.098015269f, 11.7273076323f, 282.707060357f)); + eulersFromEngineer.push_back(glm::vec3(-5.86440719705f, -11.5976507143f, 93.7305032918f)); + eulersFromEngineer.push_back(glm::vec3(212.162310762f, 11.1889030401f, 90.489887929f)); + eulersFromEngineer.push_back(glm::vec3(-183.528298852f, 11.1630787238f, 89.5119846423f)); + eulersFromEngineer.push_back(glm::vec3(-21.5907187136f, -1.16096242449f, 93.4017341288f)); + eulersFromEngineer.push_back(glm::vec3(-230.174545226f, -1.16165879865f, 86.5984269351f)); + eulersFromEngineer.push_back(glm::vec3(-71.3250132734f, 179.345867731f, -73.9245897857f)); + eulersFromEngineer.push_back(glm::vec3(4.4183033408f, 0.0214780500113f, 89.6156394563f)); + eulersFromEngineer.push_back(glm::vec3(-71.3557521294f, -1.96233777224f, 101.311991763f)); + eulersFromEngineer.push_back(glm::vec3(4.52300317986f, -3.2744480538f, 93.9297689938f)); + eulersFromEngineer.push_back(glm::vec3(183.372659045f, 18.4240672177f, 289.089833888f)); + eulersFromEngineer.push_back(glm::vec3(4.69575782766f, -18.4271517191f, 85.8795582505f)); + eulersFromEngineer.push_back(glm::vec3(180.000000381f, 184.227605816f, -90.0000102753f)); + eulersFromEngineer.push_back(glm::vec3(180.000018095f, -4.74360667058f, -89.9994155926f)); + eulersFromEngineer.push_back(glm::vec3(-6.65059725988f, 179.738939306f, -74.3826161322f)); + eulersFromEngineer.push_back(glm::vec3(1.39276173327f, 0.376166992987f, 92.9332563713f)); + eulersFromEngineer.push_back(glm::vec3(290.266370725f, -2.98535083963f, 105.944099833f)); + eulersFromEngineer.push_back(glm::vec3(1.43200712942f, -2.97286285904f, 93.1492284976f)); + eulersFromEngineer.push_back(glm::vec3(194.564263389f, 20.3832505773f, 294.645037769f)); + eulersFromEngineer.push_back(glm::vec3(1.63629620002f, -20.9266621269f, 88.1506158063f)); + eulersFromEngineer.push_back(glm::vec3(-0.0431167069757f, 179.97446471f, -73.261703364f)); + eulersFromEngineer.push_back(glm::vec3(7.60338271732f, -0.342204658844f, 91.3116554499f)); + eulersFromEngineer.push_back(glm::vec3(279.206234833f, -3.4007859075f, 106.246009192f)); + eulersFromEngineer.push_back(glm::vec3(7.72548545025f, -3.33436960581f, 92.0998444782f)); + eulersFromEngineer.push_back(glm::vec3(197.45405684f, 22.6995116972f, 295.695981195f)); + eulersFromEngineer.push_back(glm::vec3(8.21527813645f, -19.794697098f, 86.2545417524f)); + eulersFromEngineer.push_back(glm::vec3(180.000036925f, 168.400465599f, -90.0003196145f)); + eulersFromEngineer.push_back(glm::vec3(7.86635710548e-05f, -2.45210839758f, 89.9992192762f)); + eulersFromEngineer.push_back(glm::vec3(179.999920323f, 4.95709895474f, 269.999215822f)); + eulersFromEngineer.push_back(glm::vec3(241.077352225f, -184.455008741f, -426.541304554f)); + eulersFromEngineer.push_back(glm::vec3(-70.6642196779f, 6.88023375774f, 85.5052784744f)); + eulersFromEngineer.push_back(glm::vec3(51.5850104459f, 4.10357301154f, 96.1370118091f)); + eulersFromEngineer.push_back(glm::vec3(-70.3665845407f, 5.50499763056f, 102.700969646f)); + eulersFromEngineer.push_back(glm::vec3(94.1058470618f, -4.50566341059f, -63.663409373f)); + eulersFromEngineer.push_back(glm::vec3(-70.8795528982f, 7.23642133863f, 82.4215992116f)); + eulersFromEngineer.push_back(glm::vec3(354.986036508f, -0.167802950666f, -73.0700479208f)); + eulersFromEngineer.push_back(glm::vec3(174.972844269f, 0.163203627576f, 73.0320316709f)); + eulersFromEngineer.push_back(glm::vec3(-156.40949683f, -2.25584901219f, 89.478726037f)); + eulersFromEngineer.push_back(glm::vec3(-147.791270237f, -2.25609369727f, 90.5216948763f)); + + qCDebug(animation) << "list of joint quats for engineer"; + for (glm::vec3 bone : eulersFromEngineer) { + glm::quat boneQuat(glm::vec3((bone.x / 180.0f)*PI, (bone.y / 180.0f)*PI, (bone.z / 180.0f)*PI)); + qCDebug(animation) << boneQuat; + } + + + + //qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; + //glm::quat leftShoulder(glm::vec3((-81.1050620752f / 180.0f)*PI, (-20.1443891394f / 180.0f)*PI, (100.309499473f / 180.0f)*PI)); + //qCDebug(animation) << "jointRotationOffset = LeftShoulder = (" << leftShoulder << ")"; + //glm::quat testRot2(glm::vec3(PI / 2.0f, 0.0f, PI / 2.0f)); + //qCDebug(animation) << "test rot2 from euler" << testRot2; } const QMap& Rig::getJointRotationOffsets() const { diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index b818ae5da1..d1520d81f9 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,14 +54,11 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); -<<<<<<< HEAD - std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - QMap jointRotationOffsets; - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry, jointRotationOffsets)); -======= + std::unique_ptr geometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*geometry)); ->>>>>>> upstream/master + QMap jointRotationOffsets; + std::unique_ptr skeleton(new AnimSkeleton(*geometry, jointRotationOffsets)); + skeleton->dump(verbose); } From e36ab9efd682bc2baf3557ebfb24f0303b09fd4d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 00:44:52 -0800 Subject: [PATCH 24/60] cleaned up for merge with luis --- .../src/avatars/ScriptableAvatar.cpp | 3 +- interface/src/avatar/MySkeletonModel.cpp | 1 - libraries/animation/src/AnimClip.cpp | 3 +- libraries/animation/src/AnimSkeleton.cpp | 6 +- libraries/animation/src/Rig.cpp | 98 +------------------ libraries/animation/src/Rig.h | 6 -- .../src/avatars-renderer/SkeletonModel.cpp | 3 - libraries/fbx/src/FSTReader.cpp | 24 ----- libraries/fbx/src/FSTReader.h | 3 - tools/skeleton-dump/src/SkeletonDumpApp.cpp | 3 +- 10 files changed, 8 insertions(+), 142 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index a211fe1bbe..51038a782f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -84,8 +84,7 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - QMap jointRotationOffsets; - _animSkeleton = std::make_shared(_bind->getHFMModel(), jointRotationOffsets); + _animSkeleton = std::make_shared(_bind->getHFMModel()); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index a5089dc0ea..720bbd1066 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -92,7 +92,6 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const HFMModel& hfmModel = getHFMModel(); - const QMap jointOffsetMap = _rig.getJointRotationOffsets(); Head* head = _owningAvatar->getHead(); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 4900f414b7..a3d55726d6 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -103,8 +103,7 @@ void AnimClip::copyFromNetworkAnim() { // by matching joints with the same name. const HFMModel& hfmModel = _networkAnim->getHFMModel(); - QMap jointRotationOffsets; - AnimSkeleton animSkeleton(hfmModel, jointRotationOffsets); + AnimSkeleton animSkeleton(hfmModel); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index af0ec0d5b0..f0219eb946 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -17,7 +17,7 @@ #include "AnimationLogging.h" static bool notBound = true; -AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap jointOffsets) { +AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; @@ -26,7 +26,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel, const QMap joints.push_back(joint); } - buildSkeletonFromJoints(joints, jointOffsets); + buildSkeletonFromJoints(joints, hfmModel.jointOffsets); // add offsets for spine2 and the neck @@ -203,7 +203,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } - void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { _joints = joints; _jointsSize = (int)joints.size(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 28ef0aef5c..85a19f6cfe 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -311,7 +311,7 @@ void Rig::reset(const HFMModel& hfmModel) { _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(hfmModel, _jointRotationOffsets); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); @@ -2064,97 +2064,3 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = capsuleCenter - hipsPosition; } - -void Rig::setJointRotationOffsets(const QMap& offsets) { - _jointRotationOffsets.clear(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - glm::quat rotationOffset = itr.value(); - int jointIndex = indexOfJoint(jointName); - if (jointIndex != -1) { - _jointRotationOffsets.insert(jointIndex, rotationOffset); - } - qDebug() << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; - } - int neckId = 62; - int spine2Id = 13; - if (true){ //neckIndex != -1) { - _jointRotationOffsets.insert(neckId, glm::quat(0.0f, 0.7071f, 0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f) ); //glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) - qCDebug(animation) << "multiplied quats are " << glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f) * glm::quat(0.5f, 0.5f, 0.5f, -0.5f); - } - if (true){ //spine2Index != -1) { - _jointRotationOffsets.insert(spine2Id, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - } - - std::vector eulersFromEngineer; - eulersFromEngineer.push_back(glm::vec3(-81.1050620752f, -20.1443891394f, 100.309499473f)); - eulersFromEngineer.push_back(glm::vec3(260.889773298f, -20.143735467f, 79.7184972987f)); - eulersFromEngineer.push_back(glm::vec3(-196.885439853f, -15.4618394328f, 107.273377257f)); - eulersFromEngineer.push_back(glm::vec3(185.169288353f, -14.3865564204f, 73.5514142013f)); - eulersFromEngineer.push_back(glm::vec3(85.0436206363f, -0.405424354654f, 279.126747746f)); - eulersFromEngineer.push_back(glm::vec3(265.040827938f, -24.8809133357f, 80.8874530433f)); - eulersFromEngineer.push_back(glm::vec3(0.0000012746f, -9.5408567955f, 89.9999827841f)); - eulersFromEngineer.push_back(glm::vec3(-304.676178613f, -1.77806457464f, 94.6267844984f)); - eulersFromEngineer.push_back(glm::vec3(299.239708621f, -1.75410019523f, 85.3712190608f)); - eulersFromEngineer.push_back(glm::vec3(-70.5442845855f, 180.00795643f, -73.899282669f)); - eulersFromEngineer.push_back(glm::vec3(-5.65981473995f, -0.554837824572f, 90.3818083467f)); - eulersFromEngineer.push_back(glm::vec3(282.516400893f, -2.86457680169f, 106.966109268f)); - eulersFromEngineer.push_back(glm::vec3(-5.74446362761f, -3.69252247483f, 89.4088276443f)); - eulersFromEngineer.push_back(glm::vec3(175.098015269f, 11.7273076323f, 282.707060357f)); - eulersFromEngineer.push_back(glm::vec3(-5.86440719705f, -11.5976507143f, 93.7305032918f)); - eulersFromEngineer.push_back(glm::vec3(212.162310762f, 11.1889030401f, 90.489887929f)); - eulersFromEngineer.push_back(glm::vec3(-183.528298852f, 11.1630787238f, 89.5119846423f)); - eulersFromEngineer.push_back(glm::vec3(-21.5907187136f, -1.16096242449f, 93.4017341288f)); - eulersFromEngineer.push_back(glm::vec3(-230.174545226f, -1.16165879865f, 86.5984269351f)); - eulersFromEngineer.push_back(glm::vec3(-71.3250132734f, 179.345867731f, -73.9245897857f)); - eulersFromEngineer.push_back(glm::vec3(4.4183033408f, 0.0214780500113f, 89.6156394563f)); - eulersFromEngineer.push_back(glm::vec3(-71.3557521294f, -1.96233777224f, 101.311991763f)); - eulersFromEngineer.push_back(glm::vec3(4.52300317986f, -3.2744480538f, 93.9297689938f)); - eulersFromEngineer.push_back(glm::vec3(183.372659045f, 18.4240672177f, 289.089833888f)); - eulersFromEngineer.push_back(glm::vec3(4.69575782766f, -18.4271517191f, 85.8795582505f)); - eulersFromEngineer.push_back(glm::vec3(180.000000381f, 184.227605816f, -90.0000102753f)); - eulersFromEngineer.push_back(glm::vec3(180.000018095f, -4.74360667058f, -89.9994155926f)); - eulersFromEngineer.push_back(glm::vec3(-6.65059725988f, 179.738939306f, -74.3826161322f)); - eulersFromEngineer.push_back(glm::vec3(1.39276173327f, 0.376166992987f, 92.9332563713f)); - eulersFromEngineer.push_back(glm::vec3(290.266370725f, -2.98535083963f, 105.944099833f)); - eulersFromEngineer.push_back(glm::vec3(1.43200712942f, -2.97286285904f, 93.1492284976f)); - eulersFromEngineer.push_back(glm::vec3(194.564263389f, 20.3832505773f, 294.645037769f)); - eulersFromEngineer.push_back(glm::vec3(1.63629620002f, -20.9266621269f, 88.1506158063f)); - eulersFromEngineer.push_back(glm::vec3(-0.0431167069757f, 179.97446471f, -73.261703364f)); - eulersFromEngineer.push_back(glm::vec3(7.60338271732f, -0.342204658844f, 91.3116554499f)); - eulersFromEngineer.push_back(glm::vec3(279.206234833f, -3.4007859075f, 106.246009192f)); - eulersFromEngineer.push_back(glm::vec3(7.72548545025f, -3.33436960581f, 92.0998444782f)); - eulersFromEngineer.push_back(glm::vec3(197.45405684f, 22.6995116972f, 295.695981195f)); - eulersFromEngineer.push_back(glm::vec3(8.21527813645f, -19.794697098f, 86.2545417524f)); - eulersFromEngineer.push_back(glm::vec3(180.000036925f, 168.400465599f, -90.0003196145f)); - eulersFromEngineer.push_back(glm::vec3(7.86635710548e-05f, -2.45210839758f, 89.9992192762f)); - eulersFromEngineer.push_back(glm::vec3(179.999920323f, 4.95709895474f, 269.999215822f)); - eulersFromEngineer.push_back(glm::vec3(241.077352225f, -184.455008741f, -426.541304554f)); - eulersFromEngineer.push_back(glm::vec3(-70.6642196779f, 6.88023375774f, 85.5052784744f)); - eulersFromEngineer.push_back(glm::vec3(51.5850104459f, 4.10357301154f, 96.1370118091f)); - eulersFromEngineer.push_back(glm::vec3(-70.3665845407f, 5.50499763056f, 102.700969646f)); - eulersFromEngineer.push_back(glm::vec3(94.1058470618f, -4.50566341059f, -63.663409373f)); - eulersFromEngineer.push_back(glm::vec3(-70.8795528982f, 7.23642133863f, 82.4215992116f)); - eulersFromEngineer.push_back(glm::vec3(354.986036508f, -0.167802950666f, -73.0700479208f)); - eulersFromEngineer.push_back(glm::vec3(174.972844269f, 0.163203627576f, 73.0320316709f)); - eulersFromEngineer.push_back(glm::vec3(-156.40949683f, -2.25584901219f, 89.478726037f)); - eulersFromEngineer.push_back(glm::vec3(-147.791270237f, -2.25609369727f, 90.5216948763f)); - - qCDebug(animation) << "list of joint quats for engineer"; - for (glm::vec3 bone : eulersFromEngineer) { - glm::quat boneQuat(glm::vec3((bone.x / 180.0f)*PI, (bone.y / 180.0f)*PI, (bone.z / 180.0f)*PI)); - qCDebug(animation) << boneQuat; - } - - - - //qCDebug(animation) << "set the neck and spine2 offsets " << spine2Id << " " << neckId; - //glm::quat leftShoulder(glm::vec3((-81.1050620752f / 180.0f)*PI, (-20.1443891394f / 180.0f)*PI, (100.309499473f / 180.0f)*PI)); - //qCDebug(animation) << "jointRotationOffset = LeftShoulder = (" << leftShoulder << ")"; - //glm::quat testRot2(glm::vec3(PI / 2.0f, 0.0f, PI / 2.0f)); - //qCDebug(animation) << "test rot2 from euler" << testRot2; -} - -const QMap& Rig::getJointRotationOffsets() const { - return _jointRotationOffsets; -} \ No newline at end of file diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1b5826a757..345f335f88 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -128,7 +128,6 @@ public: int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; QString nameOfJoint(int jointIndex) const; - const QMap& getJointRotationOffsets() const; void setModelOffset(const glm::mat4& modelOffsetMat); @@ -232,8 +231,6 @@ public: const AnimContext::DebugAlphaMap& getDebugAlphaMap() const { return _lastContext.getDebugAlphaMap(); } const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - - void setJointRotationOffsets(const QMap& offsets); signals: void onLoadComplete(); @@ -303,8 +300,6 @@ protected: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - QMap _jointRotationOffsets; - glm::vec3 _lastForward; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; @@ -423,7 +418,6 @@ protected: SnapshotBlendPoseHelper _hipsBlendHelper; ControllerParameters _previousControllerParameters; - bool _alreadyInitialized { false }; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 0f18d254c3..3b9e874fba 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "Avatar.h" #include "Logging.h" @@ -58,7 +57,6 @@ void SkeletonModel::initJointStates() { const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); _rig.initJointStates(hfmModel, modelOffset); { @@ -85,7 +83,6 @@ void SkeletonModel::initJointStates() { // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; - //_rig.setJointRotationOffsets(FSTReader::getJointRotationOffsets(getGeometry()->getMapping())); computeBoundingShape(); Extents meshExtents = getMeshExtents(); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 10ea9dc964..75596862d2 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -207,30 +207,6 @@ QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapp return scriptPaths; } -QMap FSTReader::getJointRotationOffsets(const QVariantHash& mapping) { - QMap jointRotationOffsets; - if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { - auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - QString line = itr.value().toString(); - auto eulerAngles = line.split(','); - if (eulerAngles.size() == 3) { - float eulerX = eulerAngles[0].mid(1).toFloat(); - float eulerY = eulerAngles[1].toFloat(); - float eulerZ = eulerAngles[2].mid(0, eulerAngles[2].size() - 1).toFloat(); - if (!isNaN(eulerX) && !isNaN(eulerY) && !isNaN(eulerZ)) { - glm::quat rotationOffset = (glm::angleAxis(eulerX * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * - glm::angleAxis(eulerY * RADIANS_PER_DEGREE, Vectors::UNIT_X) * - glm::angleAxis(eulerZ * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); - jointRotationOffsets.insert(jointName, rotationOffset); - } - } - } - } - return jointRotationOffsets; -} - QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index ef649eb4d8..4a8574f0cf 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -14,7 +14,6 @@ #include #include -#include static const QString NAME_FIELD = "name"; static const QString TYPE_FIELD = "type"; @@ -30,7 +29,6 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; static const QString SCRIPT_FIELD = "script"; -static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; class FSTReader { public: @@ -53,7 +51,6 @@ public: static ModelType predictModelType(const QVariantHash& mapping); static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); - static QMap getJointRotationOffsets(const QVariantHash& mapping); static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index d1520d81f9..2bb5c758ce 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -56,8 +56,7 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc QByteArray blob = file.readAll(); std::unique_ptr geometry(readFBX(blob, QVariantHash())); - QMap jointRotationOffsets; - std::unique_ptr skeleton(new AnimSkeleton(*geometry, jointRotationOffsets)); + std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } From dce040978cda9ef13794d48b8053fc1f1e3a8f4d Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 01:31:27 -0800 Subject: [PATCH 25/60] works two joint avatar with luis's new code. remains to do the engineer --- libraries/animation/src/AnimSkeleton.cpp | 50 ++++++------------------ libraries/animation/src/AnimSkeleton.h | 2 +- libraries/fbx/src/FBXReader.cpp | 2 + 3 files changed, 14 insertions(+), 40 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index f0219eb946..a285c100fe 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -26,7 +26,10 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { joints.push_back(joint); } - buildSkeletonFromJoints(joints, hfmModel.jointOffsets); + glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); + glm::quat offset2(0.7071f, 0.0f, 0.7071f, 0.0f); + + buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck @@ -44,15 +47,17 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset2, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } if (cluster.jointIndex == 13) { qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << jointOffsets[cluster.jointIndex] << " cluster " << cluster.jointIndex; - AnimPose localOffset(jointOffsets[cluster.jointIndex], glm::vec3()); + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1<< " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset1, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -257,40 +262,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } - /* - AnimPose newAbsPose; - if (parentIndex >= 0) { - newAbsPose = _absoluteDefaultPoses[parentIndex] * AnimPose(relDefaultPose.rot(),glm::vec3()); - } else { - newAbsPose = relDefaultPose; - } - - // putting the pipeline code is - // remember the inverse bind pose already has the offset added into it. the total effect is offset^-1 * relDefPose * offset. - // this gives us the correct transform for the joint that has been put in t-pose with an offset rotation. - //relDefaultPose = relDefaultPose * _avatarTPoseOffsets[i]; - - QString jointName = getJointName(i); - if (jointOffsets.contains(i)) { - //QString parentIndex = getJointName(parentIndex); - AnimPose localOffset(jointOffsets[i], glm::vec3()); - newAbsPose = newAbsPose * localOffset; - } - if ((parentIndex >= 0) && jointOffsets.contains(parentIndex)) { - AnimPose localParentOffset(jointOffsets[parentIndex], glm::vec3()); - newAbsPose = localParentOffset.inverse() * AnimPose(glm::quat(), relDefaultPose.trans()) * localParentOffset * AnimPose(newAbsPose.rot(), glm::vec3()); - } - - if (parentIndex >= 0) { - relDefaultPose = _absoluteDefaultPoses[parentIndex].inverse() * newAbsPose; - } - _relativeDefaultPoses.push_back(relDefaultPose); - - - _absoluteDefaultPoses.push_back(newAbsPose); - - } - */ + for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index a08276d2cb..f06813e51f 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,7 +23,7 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const HFMModel& hfmModel, const QMap jointOffsets); + explicit AnimSkeleton(const HFMModel& hfmModel); explicit AnimSkeleton(const std::vector& joints, const QMap jointOffsets); int nameToJointIndex(const QString& jointName) const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6ec7e0e73c..281759a9e9 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2027,6 +2027,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } + hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); + hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); return hfmModelPtr; } From fd365b5509e3e3b6cfa37bf4bffba95411dd8e5a Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 9 Nov 2018 07:22:36 -0800 Subject: [PATCH 26/60] added debug print to fbx reader to see if the fst is being read correctly --- libraries/animation/src/AnimSkeleton.cpp | 2 +- libraries/fbx/src/FBXReader.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index a285c100fe..1d4d662928 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,7 +27,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); - glm::quat offset2(0.7071f, 0.0f, 0.7071f, 0.0f); + glm::quat offset2(0.7071f, 0.0f, -0.7071f, 0.0f); buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 281759a9e9..6ae2b69e60 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -632,10 +632,14 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); + } } + qCDebug(modelformat) << "found an offset in fst"; } + qCDebug(modelformat) << "found an offset in fst 2"; } + qCDebug(modelformat) << "found an offset in fst 3"; return jointRotationOffsets; } From 5bb2378cd90a44e921e4b24cde6613a6e9510d77 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 9 Nov 2018 15:10:14 -0800 Subject: [PATCH 27/60] changes to get the engineer working --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 4 +-- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/AnimSkeleton.cpp | 25 ++++++++++++++++--- libraries/fbx/src/FBXReader.cpp | 4 +-- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 2fc5cc4196..f91642105f 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import stylesUit 1.0 +import "../../styles-uit" import "../../controls" -import controlsUit 1.0 as HifiControls +import "../../controls-uit" as HifiControls import "." diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 502be9d57c..8d47591d40 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -431,7 +431,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { - //_skeletonModel->reset(); + _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 1d4d662928..aec969af69 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -31,8 +31,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); // add offsets for spine2 and the neck - - + static bool once = true; + qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; + if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { + //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { + once = false; for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -44,6 +47,15 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. // this works if clusters match joints one for one. + if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + //AnimPose localOffset(offset2, glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + } + + /* if (cluster.jointIndex == 62) { qCDebug(animation) << "Neck"; @@ -55,14 +67,16 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } if (cluster.jointIndex == 13) { qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1<< " cluster " << cluster.jointIndex; + qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1 << " cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset1, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } + */ } } + } @@ -247,6 +261,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } + for (int k = 0; k < _jointsSize; k++) { int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { @@ -261,6 +276,10 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); } } + + // re-compute relative poses + //_relativeDefaultPoses = _absoluteDefaultPoses; + //convertAbsolutePosesToRelative(_relativeDefaultPoses); for (int i = 0; i < _jointsSize; i++) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6ae2b69e60..6213cc458b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2031,8 +2031,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); + //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); return hfmModelPtr; } From 92befbc5bf7371ac775f468e6969af1fd8d6745f Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 12 Nov 2018 11:12:56 -0800 Subject: [PATCH 28/60] implementing backup of cluster matrices --- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index d4072f03d7..943db4b7fe 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,6 +349,7 @@ public: Extents meshExtents; QVector animationFrames; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6213cc458b..a8cbcdcf25 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2034,6 +2034,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { + const HFMCluster& cluster = mesh.clusters.at(j); + hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex] = cluster.inverseBindMatrix; + } + } + return hfmModelPtr; } From bcd651a65dc5cbf690b1f12e4ded566fc53988a7 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 12 Nov 2018 14:39:48 -0800 Subject: [PATCH 29/60] adding the member variable to keep track of the orig bind matrices to fbx.h --- libraries/animation/src/AnimSkeleton.cpp | 4 ++++ libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 11 ++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index aec969af69..aec4ae6dda 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -52,6 +52,10 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset2, glm::vec3()); cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; + //if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].contains(cluster.jointIndex))) { + // qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex]; + //} cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 943db4b7fe..342f3c456a 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,7 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a8cbcdcf25..ed5e349957 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2033,15 +2033,20 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - + /* for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); + QMap tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex] = cluster.inverseBindMatrix; + tempBindMat.insert(cluster.jointIndex, cluster.inverseBindMatrix); + //if(hfmModel.clusterBindMatrixOriginalValues[i]) + //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; + //glm::mat4 testMat = cluster.inverseBindMatrix; } + hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } - + */ return hfmModelPtr; } From f83edf4b7ffa373c8bac88214eb1b0771ca26194 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 11:29:01 -0800 Subject: [PATCH 30/60] joint offsets working. reset working. to do: handling extra joints --- libraries/animation/src/AnimSkeleton.cpp | 12 ++++++------ libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FBXReader.cpp | 7 +++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index aec4ae6dda..790c898674 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -33,7 +33,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // add offsets for spine2 and the neck static bool once = true; qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; - if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { + //if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { once = false; for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { @@ -51,11 +51,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - //if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].contains(cluster.jointIndex))) { - // qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][cluster.jointIndex]; - //} + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { + qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; + } cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -80,7 +80,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { */ } } - } + //} diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 342f3c456a..943db4b7fe 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,7 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; + std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ed5e349957..7a49874abb 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2033,20 +2033,19 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - /* + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - QMap tempBindMat; + vector tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - tempBindMat.insert(cluster.jointIndex, cluster.inverseBindMatrix); + tempBindMat.push_back(cluster.inverseBindMatrix); //if(hfmModel.clusterBindMatrixOriginalValues[i]) //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; //glm::mat4 testMat = cluster.inverseBindMatrix; } hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } - */ return hfmModelPtr; } From c80ade98ecfcea0d1a8eeb712647a033e78cd660 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 12:10:10 -0800 Subject: [PATCH 31/60] cleaned up white space --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 4 +- interface/src/avatar/MySkeletonModel.cpp | 3 - libraries/animation/src/AnimClip.cpp | 2 - libraries/animation/src/AnimSkeleton.cpp | 84 +++++-------------- libraries/fbx/src/FBXReader.cpp | 13 +-- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 2 - 6 files changed, 24 insertions(+), 84 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index f91642105f..2fc5cc4196 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "." diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 720bbd1066..2a21f78b21 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -90,7 +90,6 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -230,7 +229,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - //qCDebug(interfaceapp) << "spine 2 joint offset " << jointOffsetMap[13]; if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { @@ -241,7 +239,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); - if (spine2Exists && headExists && hipsExists) { AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index a3d55726d6..eeac8fadce 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,10 +101,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const HFMModel& hfmModel = _networkAnim->getHFMModel(); AnimSkeleton animSkeleton(hfmModel); - const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 790c898674..85c9999788 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -15,76 +15,38 @@ #include #include "AnimationLogging.h" -static bool notBound = true; AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { - qCDebug(animation) << "in the animSkeleton"; // convert to std::vector of joints std::vector joints; joints.reserve(hfmModel.joints.size()); for (auto& joint : hfmModel.joints) { joints.push_back(joint); } - - glm::quat offset1(0.5f, 0.5f, 0.5f, -0.5f); - glm::quat offset2(0.7071f, 0.0f, -0.7071f, 0.0f); - buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); - // add offsets for spine2 and the neck - static bool once = true; - qCDebug(animation) << "the hfm model path is " << hfmModel.originalURL; - //if (once && hfmModel.originalURL == "/angus/avatars/engineer_hifinames/engineer_hifinames/engineer_hifinames.fbx") { - //if (once && hfmModel.originalURL == "/angus/avatars/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd/pei_z_neckNexX_spine2NegY_fwd.fbx") { - once = false; - for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { - const HFMMesh& mesh = hfmModel.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { - // cast into a non-const reference, so we can mutate the FBXCluster - HFMCluster& cluster = const_cast(mesh.clusters.at(j)); + // cast into a non-const reference, so we can mutate the FBXCluster + HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. - // this works if clusters match joints one for one. - if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { - qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; - } - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before + // rendering, with no runtime overhead. + if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { + qCDebug(animation) << "found a cluster " << cluster.jointIndex; + AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { + qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; } - - /* - - if (cluster.jointIndex == 62) { - qCDebug(animation) << "Neck"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset2 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset2, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - if (cluster.jointIndex == 13) { - qCDebug(animation) << "Spine2"; - qCDebug(animation) << "found a joint offset to add " << cluster.jointIndex << " " << offset1 << " cluster " << cluster.jointIndex; - AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - //AnimPose localOffset(offset1, glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); - } - */ + cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } } - //} - - - -} + } +} AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { buildSkeletonFromJoints(joints, jointOffsets); @@ -253,19 +215,15 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); - AnimPose newAbsPose; if (parentIndex >= 0) { newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; - _absoluteDefaultPoses.push_back(newAbsPose); } else { - _absoluteDefaultPoses.push_back(relDefaultPose); } - } - + for (int k = 0; k < _jointsSize; k++) { int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { @@ -273,19 +231,15 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; } if (parentIndex2 >= 0) { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); } else { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); } } - - // re-compute relative poses + // re-compute relative poses //_relativeDefaultPoses = _absoluteDefaultPoses; //convertAbsolutePosesToRelative(_relativeDefaultPoses); - for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 7a49874abb..da0fe4fe01 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -632,14 +632,10 @@ QMap getJointRotationOffsets(const QVariantHash& mapping) { if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); jointRotationOffsets.insert(jointName, rotationOffset); - } } - qCDebug(modelformat) << "found an offset in fst"; } - qCDebug(modelformat) << "found an offset in fst 2"; } - qCDebug(modelformat) << "found an offset in fst 3"; return jointRotationOffsets; } @@ -2031,18 +2027,15 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - //hfmModel.jointRotationOffsets.insert(13, glm::quat(0.5f, 0.5f, 0.5f, -0.5f)); - //hfmModel.jointRotationOffsets.insert(62, glm::quat(0.7071f, 0.0f, -0.7071f, 0.0f)); - + + // create a backup copy of the bindposes, + // these are needed when we recompute the bindpose offsets on reset. for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); vector tempBindMat; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); tempBindMat.push_back(cluster.inverseBindMatrix); - //if(hfmModel.clusterBindMatrixOriginalValues[i]) - //hfmModel.clusterBindMatrixOriginalValues[i].insert(cluster.jointIndex,Matrices::IDENTITY);// cluster.inverseBindMatrix; - //glm::mat4 testMat = cluster.inverseBindMatrix; } hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); } diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index 2bb5c758ce..10b13aef36 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,10 +54,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr geometry(readFBX(blob, QVariantHash())); std::unique_ptr skeleton(new AnimSkeleton(*geometry)); - skeleton->dump(verbose); } From dab025304d23c6af5e5bfaaa165841cb79148c52 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 13 Nov 2018 12:22:30 -0800 Subject: [PATCH 32/60] Set chunk size to 1 MB --- domain-server/resources/web/content/js/content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 38f93a57be..3f36a1b447 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -23,7 +23,7 @@ $(document).ready(function(){ var fileSize = file.size; var filename = file.name; - var CHUNK_SIZE = 65536; + var CHUNK_SIZE = 1048576; // 1 MiB var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); From a8d7b0503d10f6d0e7b2104f1f7e921e2558a8f1 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 13 Nov 2018 14:24:18 -0800 Subject: [PATCH 33/60] removed more cruft --- libraries/animation/src/AnimSkeleton.cpp | 21 +++++---------------- libraries/fbx/src/FBXReader.cpp | 6 +++--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 85c9999788..a9dab701ed 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -35,12 +35,9 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { - qCDebug(animation) << "found a cluster " << cluster.jointIndex; AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - qCDebug(animation) << "the new bind matrix num: " << cluster.jointIndex << cluster.inverseBindMatrix; - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > cluster.jointIndex)) { - qCDebug(animation) << "the saved orig matrix num: " << cluster.jointIndex << hfmModel.clusterBindMatrixOriginalValues[i][j]; + if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { + cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); } @@ -215,30 +212,22 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); - AnimPose newAbsPose; if (parentIndex >= 0) { - newAbsPose = _absoluteDefaultPoses[parentIndex] * relDefaultPose; - _absoluteDefaultPoses.push_back(newAbsPose); + _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { _absoluteDefaultPoses.push_back(relDefaultPose); } } for (int k = 0; k < _jointsSize; k++) { - int parentIndex2 = getParentIndex(k); if (jointOffsets.contains(k)) { AnimPose localOffset(jointOffsets[k], glm::vec3()); _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; } - if (parentIndex2 >= 0) { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex2].inverse() * _absoluteDefaultPoses[k]); - } else { - _relativeDefaultPoses.push_back(_absoluteDefaultPoses[k]); - } } // re-compute relative poses - //_relativeDefaultPoses = _absoluteDefaultPoses; - //convertAbsolutePosesToRelative(_relativeDefaultPoses); + _relativeDefaultPoses = _absoluteDefaultPoses; + convertAbsolutePosesToRelative(_relativeDefaultPoses); for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index da0fe4fe01..2e4f98ee4c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2032,12 +2032,12 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& // these are needed when we recompute the bindpose offsets on reset. for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - vector tempBindMat; + vector meshBindMatrices; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); - tempBindMat.push_back(cluster.inverseBindMatrix); + meshBindMatrices.push_back(cluster.inverseBindMatrix); } - hfmModel.clusterBindMatrixOriginalValues.push_back(tempBindMat); + hfmModel.clusterBindMatrixOriginalValues.push_back(meshBindMatrices); } return hfmModelPtr; } From f5e14565b804f92c9a86a55edf8a23a015b92bfa Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 14 Nov 2018 10:17:48 -0800 Subject: [PATCH 34/60] Clean-up javascript formatting --- .../resources/web/content/js/content.js | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 3f36a1b447..93bde3122b 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -14,17 +14,16 @@ $(document).ready(function(){ return html; } - function uploadNextChunk(file, offset) - { + function uploadNextChunk(file, offset) { if (offset == undefined) { offset = 0; } var fileSize = file.size; var filename = file.name; - + var CHUNK_SIZE = 1048576; // 1 MiB - + var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); @@ -33,29 +32,29 @@ $(document).ready(function(){ var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; chunkFormData.append(formItemName, chunk, filename); var ajaxParams = { - url: '/content/upload', - type: 'POST', - timeout: 30000, // 30 s - cache: false, - processData: false, - contentType: false, - data: chunkFormData - }; - + url: '/content/upload', + type: 'POST', + timeout: 30000, // 30 s + cache: false, + processData: false, + contentType: false, + data: chunkFormData + }; + var ajaxObject = $.ajax(ajaxParams); ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { - showErrorMessage( - "Error", - "There was a problem restoring domain content.\n" - + "Please ensure that the content archive or entity file is valid and try again." - ); - }); + showErrorMessage( + "Error", + "There was a problem restoring domain content.\n" + + "Please ensure that the content archive or entity file is valid and try again." + ); + }); if (!isFinal) { - ajaxObject.done(function (data, textStatus, jqXHR) - {uploadNextChunk(file, offset + CHUNK_SIZE);}); + ajaxObject.done(function (data, textStatus, jqXHR) + { uploadNextChunk(file, offset + CHUNK_SIZE); }); } else { - ajaxObject.done(function(data, textStatus, jqXHR) { + ajaxObject.done(function(data, textStatus, jqXHR) { isRestoring = true; // immediately reload backup information since one should be restoring now @@ -65,7 +64,7 @@ $(document).ready(function(){ }); } - } + } function setupBackupUpload() { // construct the HTML needed for the settings backup panel @@ -106,7 +105,6 @@ $(document).ready(function(){ showSpinnerAlert("Uploading content to restore"); - uploadNextChunk(files[0]); } ); From f17723c216520c481b30db69676fe57c35b71d54 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 14 Nov 2018 14:50:38 -0800 Subject: [PATCH 35/60] disable interstitial for master --- libraries/networking/src/DomainHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index c0c5a4d059..620ffb9641 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -234,7 +234,7 @@ private: #ifdef Q_OS_ANDROID Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; #else - Setting::Handle _enableInterstitialMode { "enableInterstitialMode", true }; + Setting::Handle _enableInterstitialMode { "enableInterstitialMode", false }; #endif QSet _domainConnectionRefusals; From 8256c652fc7dd2d919f3e957659970fce41df23e Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 14 Nov 2018 17:02:25 -0800 Subject: [PATCH 36/60] added the hook to put the copy of the mutated bind poses into tthe skeleton --- libraries/animation/src/AnimSkeleton.cpp | 19 +++++++++++++---- libraries/animation/src/AnimSkeleton.h | 2 ++ libraries/fbx/src/FBXReader.cpp | 2 +- .../render-utils/src/CauterizedModel.cpp | 21 +++++++++++++++---- libraries/render-utils/src/Model.cpp | 10 +++++++-- .../render-utils/src/SoftAttachmentModel.cpp | 7 ++++--- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index a9dab701ed..d9c90a58cc 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,21 +27,32 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); - for (int j = 0; j < mesh.clusters.size(); j++) { + std::vector dummyClustersList; + for (int j = 0; j < mesh.clusters.size(); j++) { + std::vector bindMatrices; // cast into a non-const reference, so we can mutate the FBXCluster HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - + + HFMCluster localCluster; + localCluster.jointIndex = cluster.jointIndex; + localCluster.inverseBindMatrix = cluster.inverseBindMatrix; + localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); + //dummyClustersList.push_back(localCluster); // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before // rendering, with no runtime overhead. if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { - cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; + //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } - cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); + localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } + dummyClustersList.push_back(localCluster); } + _clusterBindMatrixOriginalValues.push_back(dummyClustersList); } } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index f06813e51f..552b40b135 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -63,6 +63,7 @@ public: void dump(const AnimPoseVec& poses) const; std::vector lookUpJointIndices(const std::vector& jointNames) const; + std::vector> _clusterBindMatrixOriginalValues; protected: void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); @@ -77,6 +78,7 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; + // std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2e4f98ee4c..5ac87e3004 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2029,7 +2029,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } // create a backup copy of the bindposes, - // these are needed when we recompute the bindpose offsets on reset. + // these are needed when we recompute the bindpose offsets in the AnimSkeleton constructor on mySkeleton->reset() for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); vector meshBindMatrices; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index c31345bc55..eed5c16445 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,6 +112,7 @@ void CauterizedModel::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { + qCDebug(renderutils) << "mesh states size " << _meshStates.size() << " mesh size " << hfmModel.meshes.size(); Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -120,12 +121,20 @@ void CauterizedModel::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform clusterTransform2; + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform2, jointTransform, cluster.inverseBindTransform); + //qCDebug(renderutils) << "the animskel matrix i " << i << " j " << j << _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform; + qCDebug(renderutils) << "the mesh state cluster matrix " << state.clusterMatrices[j]; + //qCDebug(renderutils) << "cluster transform old way " << clusterTransform2; + //qCDebug(renderutils) << "cluster transform new way " << clusterTransform; state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -157,7 +166,8 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); } @@ -166,7 +176,8 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + // glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -231,6 +242,8 @@ void CauterizedModel::updateRenderItems() { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); + //qCDebug(renderutils) << "mesh cluster transform " << meshState.clusterDualQuaternions[0]; + //qCDebug(renderutils) << "cauterized mesh cluster transform " << cauterizedMeshState.clusterDualQuaternions[0]; } else { data.updateClusterBuffer(meshState.clusterMatrices, cauterizedMeshState.clusterMatrices); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7da7a45e83..25dba9c1de 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -237,6 +237,10 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); + if (useDualQuaternionSkinning) { + qCDebug(renderutils) << "use dual quats value " << useDualQuaternionSkinning; + } + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { @@ -1425,11 +1429,13 @@ void Model::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + // glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index f26bad86b0..58b0989403 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -61,7 +61,8 @@ void SoftAttachmentModel::updateClusterMatrices() { } glm::mat4 m; - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { glm::mat4 jointMatrix; @@ -70,8 +71,8 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } } From 80ebde10369f576359834c073355c5de13524fcb Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 14 Nov 2018 17:27:45 -0800 Subject: [PATCH 37/60] started cleanup of pr --- libraries/animation/src/AnimSkeleton.cpp | 10 +++------- libraries/animation/src/AnimSkeleton.h | 1 - .../src/avatars-renderer/SkeletonModel.cpp | 1 - libraries/fbx/src/FBXReader.cpp | 11 ----------- libraries/render-utils/src/CauterizedModel.cpp | 13 ------------- libraries/render-utils/src/Model.cpp | 4 ---- libraries/render-utils/src/SoftAttachmentModel.cpp | 2 -- 7 files changed, 3 insertions(+), 39 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index d9c90a58cc..435eaf9184 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -33,21 +33,18 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { std::vector bindMatrices; // cast into a non-const reference, so we can mutate the FBXCluster HFMCluster& cluster = const_cast(mesh.clusters.at(j)); - + HFMCluster localCluster; localCluster.jointIndex = cluster.jointIndex; localCluster.inverseBindMatrix = cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); - //dummyClustersList.push_back(localCluster); - // AJT: mutate bind pose! this allows us to oreint the skeleton back into the authored orientaiton before - // rendering, with no runtime overhead. + + // we make a copy of the inversebindMatrices in order to prevent mutating the model bind pose if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - //cluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; } - //cluster.inverseBindTransform.evalFromRawMatrix(cluster.inverseBindMatrix); localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } dummyClustersList.push_back(localCluster); @@ -220,7 +217,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); - qCDebug(animation) << "relative default pose for joint " << i << " " << relDefaultPose.trans() << " " << relDefaultPose.rot(); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 552b40b135..7436609ee1 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -78,7 +78,6 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; - // std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 32c95b8c4c..36e37dd3d4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -56,7 +56,6 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { void SkeletonModel::initJointStates() { const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(hfmModel, modelOffset); { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5ac87e3004..6ec7e0e73c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2028,17 +2028,6 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - // create a backup copy of the bindposes, - // these are needed when we recompute the bindpose offsets in the AnimSkeleton constructor on mySkeleton->reset() - for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { - const HFMMesh& mesh = hfmModel.meshes.at(i); - vector meshBindMatrices; - for (int j = 0; j < mesh.clusters.size(); j++) { - const HFMCluster& cluster = mesh.clusters.at(j); - meshBindMatrices.push_back(cluster.inverseBindMatrix); - } - hfmModel.clusterBindMatrixOriginalValues.push_back(meshBindMatrices); - } return hfmModelPtr; } diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index eed5c16445..e00a82d71f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -112,7 +112,6 @@ void CauterizedModel::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { - qCDebug(renderutils) << "mesh states size " << _meshStates.size() << " mesh size " << hfmModel.meshes.size(); Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -121,19 +120,11 @@ void CauterizedModel::updateClusterMatrices() { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform clusterTransform2; - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); - Transform::mult(clusterTransform2, jointTransform, cluster.inverseBindTransform); - //qCDebug(renderutils) << "the animskel matrix i " << i << " j " << j << _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform; - qCDebug(renderutils) << "the mesh state cluster matrix " << state.clusterMatrices[j]; - //qCDebug(renderutils) << "cluster transform old way " << clusterTransform2; - //qCDebug(renderutils) << "cluster transform new way " << clusterTransform; state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } @@ -166,7 +157,6 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); @@ -176,7 +166,6 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - // glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } @@ -242,8 +231,6 @@ void CauterizedModel::updateRenderItems() { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); - //qCDebug(renderutils) << "mesh cluster transform " << meshState.clusterDualQuaternions[0]; - //qCDebug(renderutils) << "cauterized mesh cluster transform " << cauterizedMeshState.clusterDualQuaternions[0]; } else { data.updateClusterBuffer(meshState.clusterMatrices, cauterizedMeshState.clusterMatrices); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 25dba9c1de..a5565d0646 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -237,10 +237,6 @@ void Model::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - if (useDualQuaternionSkinning) { - qCDebug(renderutils) << "use dual quats value " << useDualQuaternionSkinning; - } - transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 58b0989403..e45141a838 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -61,7 +61,6 @@ void SoftAttachmentModel::updateClusterMatrices() { } glm::mat4 m; - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { @@ -71,7 +70,6 @@ void SoftAttachmentModel::updateClusterMatrices() { } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - //glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); } } From cd00abd2163583b68296443e30f1c113218bff15 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 15 Nov 2018 09:47:43 -0800 Subject: [PATCH 38/60] Add upload progress bar --- .../resources/web/content/js/content.js | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 93bde3122b..78e78266d3 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -10,10 +10,20 @@ $(document).ready(function(){ function progressBarHTML(extraClass, label) { var html = "
"; html += "
"; - html += label + "
"; + html += label + "
"; return html; } + function showUploadProgress(title) { + swal({ + title: title, + text: progressBarHTML('upload-content-progress', 'Upload'), + html: true, + showConfirmButton: false, + allowEscapeKey: false + }); + } + function uploadNextChunk(file, offset) { if (offset == undefined) { offset = 0; @@ -26,7 +36,7 @@ $(document).ready(function(){ var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE); var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE); - var chunk = file.slice(offset, offset + CHUNK_SIZE, file.type); + var chunk = file.slice(offset, offset + nextChunkSize, file.type); var chunkFormData = new FormData(); var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; @@ -50,6 +60,8 @@ $(document).ready(function(){ ); }); + updateProgressBars($('.upload-content-progress'), Math.round((offset + nextChunkSize) * 100 / fileSize)); + if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) { uploadNextChunk(file, offset + CHUNK_SIZE); }); @@ -102,10 +114,10 @@ $(document).ready(function(){ "Restore content", function() { var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files'); + var file = files[0]; - showSpinnerAlert("Uploading content to restore"); - - uploadNextChunk(files[0]); + showUploadProgress("Uploading " + file.name); + uploadNextChunk(file); } ); }); @@ -196,6 +208,11 @@ $(document).ready(function(){ checkBackupStatus(); }); + function updateProgressBars($progressBar, value) { + $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); + $progressBar.find('.ongoing-msg').html(" " + value + "% Complete"); + } + function reloadBackupInformation() { // make a GET request to get backup information to populate the table $.ajax({ @@ -232,11 +249,6 @@ $(document).ready(function(){ + "
  • Delete
  • "; } - function updateProgressBars($progressBar, value) { - $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.sr-only').html(value + "% Complete"); - } - // before we add any new rows and update existing ones // remove our flag for active rows $('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS); From c8cd65c3bd46d17668f4c94042fed45b2b0fa199 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 15 Nov 2018 09:57:09 -0800 Subject: [PATCH 39/60] added getter for the orginal cluster bind matrix values in AnimSkeleton --- libraries/animation/src/AnimSkeleton.cpp | 9 ++++--- libraries/animation/src/AnimSkeleton.h | 3 ++- libraries/fbx/src/FBX.h | 1 - .../render-utils/src/CauterizedModel.cpp | 14 +++++++--- libraries/render-utils/src/Model.cpp | 9 ++++--- .../render-utils/src/SoftAttachmentModel.cpp | 26 +++++++------------ 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 435eaf9184..91ca2359b4 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -25,6 +25,8 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); + // we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose + // when we are dealing with a joint offset in the model for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { const HFMMesh& mesh = hfmModel.meshes.at(i); std::vector dummyClustersList; @@ -39,12 +41,11 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { localCluster.inverseBindMatrix = cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); - // we make a copy of the inversebindMatrices in order to prevent mutating the model bind pose + // if we have a joint offset in the fst file then multiply its inverse by the + // model cluster inverse bind matrix if (hfmModel.jointRotationOffsets.contains(cluster.jointIndex)) { AnimPose localOffset(hfmModel.jointRotationOffsets[cluster.jointIndex], glm::vec3()); - if ((hfmModel.clusterBindMatrixOriginalValues.size() > i) && (hfmModel.clusterBindMatrixOriginalValues[i].size() > j)) { - localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * hfmModel.clusterBindMatrixOriginalValues[i][j]; - } + localCluster.inverseBindMatrix = (glm::mat4)localOffset.inverse() * cluster.inverseBindMatrix; localCluster.inverseBindTransform.evalFromRawMatrix(localCluster.inverseBindMatrix); } dummyClustersList.push_back(localCluster); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 7436609ee1..ab89eb643d 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -63,7 +63,7 @@ public: void dump(const AnimPoseVec& poses) const; std::vector lookUpJointIndices(const std::vector& jointNames) const; - std::vector> _clusterBindMatrixOriginalValues; + const HFMCluster getClusterBindMatricesOriginalValues(const int meshIndex, const int clusterIndex) const { return _clusterBindMatrixOriginalValues[meshIndex][clusterIndex]; } protected: void buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets); @@ -78,6 +78,7 @@ protected: std::vector _nonMirroredIndices; std::vector _mirrorMap; QHash _jointIndicesByName; + std::vector> _clusterBindMatrixOriginalValues; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 943db4b7fe..d4072f03d7 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -349,7 +349,6 @@ public: Extents meshExtents; QVector animationFrames; - std::vector> clusterBindMatrixOriginalValues; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index e00a82d71f..86d4793aa5 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -114,18 +114,22 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; + for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; + if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans()); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -146,9 +150,11 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); + int meshIndex = i; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; if (_useDualQuaternionSkinning) { if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { @@ -157,7 +163,7 @@ void CauterizedModel::updateClusterMatrices() { } else { Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans()); } @@ -166,7 +172,7 @@ void CauterizedModel::updateClusterMatrices() { // not cauterized so just copy the value from the non-cauterized version. state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j]; } else { - glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(cauterizeMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a5565d0646..9cefbf65a8 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1418,20 +1418,21 @@ void Model::updateClusterMatrices() { const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; + int meshIndex = i; const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; + if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); Transform clusterTransform; - Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindTransform); - //Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform); } else { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); - // glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index e45141a838..a6f82e1417 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -46,31 +46,25 @@ void SoftAttachmentModel::updateClusterMatrices() { for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const HFMMesh& mesh = hfmModel.meshes.at(i); - + int meshIndex = i; for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); + int clusterIndex = j; // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } if (_useDualQuaternionSkinning) { - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - glm::mat4 m; - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, m); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, m); state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m); } else { - glm::mat4 jointMatrix; - if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { - jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); - } else { - jointMatrix = _rig.getJointTransform(cluster.jointIndex); - } - glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->_clusterBindMatrixOriginalValues[i][j].inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindMatrix, state.clusterMatrices[j]); } } } From 3bf4a7844d6d17b93e54f12f072b55d7796c6380 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 15 Nov 2018 10:56:17 -0800 Subject: [PATCH 40/60] moved the jointrotationoffsets to hfmModel.h from fbx.h --- libraries/hfm/src/hfm/HFM.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 7d4479e681..05e48b6534 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -311,6 +311,8 @@ public: QString getModelNameOfMesh(int meshIndex) const; QList blendshapeChannelNames; + + QMap jointRotationOffsets; }; }; From 556aec94121babbc47a03e1d38ff052bffd29a1a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Nov 2018 15:27:30 -0800 Subject: [PATCH 41/60] Fix hiding of properties in Create --- scripts/system/html/css/edit-style.css | 12 +++++++++++- scripts/system/html/js/entityProperties.js | 14 ++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 3a291bf27b..bec926ad03 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1559,7 +1559,9 @@ input.rename-entity { .container > label { margin-top: 6px; - width: 200px; + width: 160px; + min-width: 160px; + max-width: 160px; } .container > div.checkbox { @@ -1650,3 +1652,11 @@ input.number-slider { display: flex; width: 100%; } + +.spacemode-hidden { + display: none; +} + +#placeholder-property-type { + min-width: 0px; +} diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c49e0d88f6..a4cde98abe 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -29,7 +29,7 @@ const ICON_FOR_TYPE = { const DEGREES_TO_RADIANS = Math.PI / 180.0; -const NO_SELECTION = "w"; +const NO_SELECTION = ","; const PROPERTY_SPACE_MODE = { ALL: 0, @@ -1244,8 +1244,9 @@ const GROUPS = [ showPropertyRule: { "collisionless": "false" }, }, { - label: "Collision sound URL", + label: "Collision Sound", type: "string", + placeholder: "URL", propertyID: "collisionSoundURL", showPropertyRule: { "collisionless": "false" }, }, @@ -1500,7 +1501,7 @@ function disableProperties() { function showPropertyElement(propertyID, show) { let elProperty = properties[propertyID].elContainer; - elProperty.style.display = show ? "flex" : "none"; + elProperty.style.display = show ? "" : "none"; } function resetProperties() { @@ -1622,10 +1623,11 @@ function updateVisibleSpaceModeProperties() { if (properties.hasOwnProperty(propertyID)) { let property = properties[propertyID]; let propertySpaceMode = property.spaceMode; - if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL) { - showPropertyElement(propertyID, propertySpaceMode === currentSpaceMode); + let elProperty = properties[propertyID].elContainer; + if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL && propertySpaceMode !== currentSpaceMode) { + elProperty.classList.add('spacemode-hidden'); } else { - showPropertyElement(propertyID, true); + elProperty.classList.remove('spacemode-hidden'); } } } From 22c3f5239adbc48fbd3ed05b4617a8f09cd1a0b1 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 15 Nov 2018 16:59:52 -0800 Subject: [PATCH 42/60] Relay upoaded content.zip chunks to temp file Entities uploads still build in-memory. Move out chunk handling to new routine. --- .../resources/web/content/js/content.js | 4 +- .../src/DomainContentBackupManager.cpp | 21 +++++ .../src/DomainContentBackupManager.h | 1 + domain-server/src/DomainServer.cpp | 77 ++++++++++--------- domain-server/src/DomainServer.h | 3 +- 5 files changed, 67 insertions(+), 39 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 78e78266d3..365c5e8403 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -60,7 +60,7 @@ $(document).ready(function(){ ); }); - updateProgressBars($('.upload-content-progress'), Math.round((offset + nextChunkSize) * 100 / fileSize)); + updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize); if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) @@ -210,7 +210,7 @@ $(document).ready(function(){ function updateProgressBars($progressBar, value) { $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.ongoing-msg').html(" " + value + "% Complete"); + $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "% Complete"); } function reloadBackupInformation() { diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index 518ed73f9e..3b8180e49e 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -348,6 +348,27 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise }); } +void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise), + Q_ARG(QString, uploadedFilename)); + return; + } + + qDebug() << "Recovering from uploaded file -" << uploadedFilename; + + QFile uploadedFile(uploadedFilename); + QuaZip uploadedZip { &uploadedFile }; + + QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip"; + + bool success = recoverFromBackupZip(backupName, uploadedZip); + + promise->resolve({ + { "success", success } + }); +} + std::vector DomainContentBackupManager::getAllBackups() { QDir backupDir { _backupDirectory }; diff --git a/domain-server/src/DomainContentBackupManager.h b/domain-server/src/DomainContentBackupManager.h index 2b07afe0b3..4af3ae5bfd 100644 --- a/domain-server/src/DomainContentBackupManager.h +++ b/domain-server/src/DomainContentBackupManager.h @@ -86,6 +86,7 @@ public slots: void createManualBackup(MiniPromise::Promise promise, const QString& name); void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup); + void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename); void deleteBackup(MiniPromise::Promise promise, const QString& backupName); signals: diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 117313462b..3e68cd0fc4 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2268,17 +2268,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url uploadedFilename = formDataFieldsRegex.cap(2); } - _pendingUploadedContent += firstFormData.second; - if (formItemName == "restore-file-chunk") { - // Received another chunk - connection->respond(HTTPConnection::StatusCode200); - } else if (formItemName == "restore-file-chunk-final") { - readPendingContent(connection, uploadedFilename); - } else if (formItemName == "restore-file") { - readPendingContent(connection, uploadedFilename); - } else { - connection->respond(HTTPConnection::StatusCode400); - } + // Received a chunk + processPendingContent(connection, formItemName, uploadedFilename, firstFormData.second); } else { // respond with a 400 for failure connection->respond(HTTPConnection::StatusCode400); @@ -2527,37 +2518,51 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } -void DomainServer::readPendingContent(HTTPConnection* connection, QString filename) { - if (filename.endsWith(".json", Qt::CaseInsensitive) - || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { - // invoke our method to hand the new octree file off to the octree server - QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", - Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); +bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { + static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; - // respond with a 200 for success + if (!_pendingFileContent) { + _pendingFileContent = std::make_unique(TEMPORARY_CONTENT_FILEPATH); + } + if (!_pendingFileContent->open()) { + _pendingFileContent = nullptr; + connection->respond(HTTPConnection::StatusCode400); + return false; + } + _pendingFileContent->seek(_pendingFileContent->size()); + _pendingFileContent->write(dataChunk); + _pendingFileContent->close(); + + // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); - _pendingUploadedContent.clear(); - } else if (filename.endsWith(".zip", Qt::CaseInsensitive)) { - auto deferred = makePromise("recoverFromUploadedBackup"); + if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + auto deferred = makePromise("recoverFromUploadedBackup"); - QPointer connectionPtr(connection); - const QString JSON_MIME_TYPE = "application/json"; - deferred->then([connectionPtr, JSON_MIME_TYPE, this](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } + deferred->then([this](QString error, QVariantMap result) { + _pendingFileContent = nullptr; + }); - QJsonObject rootJSON; - auto success = result["success"].toBool(); - rootJSON["success"] = success; - QJsonDocument docJSON(rootJSON); - connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(), - JSON_MIME_TYPE.toUtf8()); + _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent->fileName()); + } else { + } + } else if (filename.endsWith(".json", Qt::CaseInsensitive) + || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + _pendingUploadedContent += dataChunk; + connection->respond(HTTPConnection::StatusCode200); + + if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); _pendingUploadedContent.clear(); - }); - - _contentManager->recoverFromUploadedBackup(deferred, _pendingUploadedContent); + } + } else { + connection->respond(HTTPConnection::StatusCode400); + return false; } + + return true; } HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index b2ef933bc3..73e23bb3fe 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -209,7 +209,7 @@ private: HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply); - void readPendingContent(HTTPConnection* connection, QString filename); + bool processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk); bool forwardMetaverseAPIRequest(HTTPConnection* connection, const QString& metaversePath, @@ -284,6 +284,7 @@ private: QHash> _pendingOAuthConnections; QByteArray _pendingUploadedContent; + std::unique_ptr _pendingFileContent; QThread _assetClientThread; }; From 5f51ed0210b24393098f9cd46620db07bb1eda8c Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 09:44:21 -0800 Subject: [PATCH 43/60] Keep pending content per remote address --- domain-server/src/DomainServer.cpp | 28 ++++++++++++++++------------ domain-server/src/DomainServer.h | 5 +++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3e68cd0fc4..670e179f81 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2521,33 +2521,37 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; + const auto peerAddressHash = qHash(connection->socket()->peerAddress()); - if (!_pendingFileContent) { - _pendingFileContent = std::make_unique(TEMPORARY_CONTENT_FILEPATH); + if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { + _pendingContentFiles.emplace(peerAddressHash, TEMPORARY_CONTENT_FILEPATH); } - if (!_pendingFileContent->open()) { - _pendingFileContent = nullptr; + + QTemporaryFile& _pendingFileContent = _pendingContentFiles[peerAddressHash]; + if (!_pendingFileContent.open()) { + _pendingContentFiles.erase(peerAddressHash); connection->respond(HTTPConnection::StatusCode400); return false; } - _pendingFileContent->seek(_pendingFileContent->size()); - _pendingFileContent->write(dataChunk); - _pendingFileContent->close(); + _pendingFileContent.seek(_pendingFileContent.size()); + _pendingFileContent.write(dataChunk); + _pendingFileContent.close(); // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { auto deferred = makePromise("recoverFromUploadedBackup"); - deferred->then([this](QString error, QVariantMap result) { - _pendingFileContent = nullptr; + deferred->then([this, peerAddressHash](QString error, QVariantMap result) { + _pendingContentFiles.erase(peerAddressHash); }); - _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent->fileName()); - } else { + _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName()); } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + auto peerAddressHash = qHash(connection->socket()->peerAddress()); + QByteArray& _pendingUploadedContent = _pendingUploadedContents[peerAddressHash]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); @@ -2555,7 +2559,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContent.clear(); + _pendingUploadedContents.erase(peerAddressHash); } } else { connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 73e23bb3fe..2d5729be59 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -283,8 +284,8 @@ private: QHash> _pendingOAuthConnections; - QByteArray _pendingUploadedContent; - std::unique_ptr _pendingFileContent; + std::unordered_map _pendingUploadedContents; + std::unordered_map _pendingContentFiles; QThread _assetClientThread; }; From a77121a5882cd6145772df5a22c0ce64c70e4c0b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 14 Nov 2018 16:12:34 -0800 Subject: [PATCH 44/60] keyboard touch ups --- interface/resources/config/keyboard.json | 36 +++++++++---------- interface/resources/meshes/drumstick.fbx | Bin 63952 -> 40496 bytes interface/resources/sounds/keyboardPress.mp3 | Bin 0 -> 7313 bytes interface/resources/sounds/keyboard_key.mp3 | Bin 9089 -> 0 bytes interface/src/ui/Keyboard.cpp | 19 +++++----- interface/src/ui/overlays/Overlays.cpp | 5 +-- 6 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 interface/resources/sounds/keyboardPress.mp3 delete mode 100644 interface/resources/sounds/keyboard_key.mp3 diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json index 186a9c1084..b3688ef06e 100644 --- a/interface/resources/config/keyboard.json +++ b/interface/resources/config/keyboard.json @@ -701,9 +701,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -752,7 +752,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -777,9 +777,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { @@ -1479,9 +1479,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -1530,7 +1530,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -1555,9 +1555,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { @@ -2305,9 +2305,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.53203323516845703, + "x": -0.59333323516845703, "y": 0.019300000742077827, - "z": -0.07286686894893646 + "z": 0.037454843521118164 }, "modelURL": "meshes/keyboard/SM_key.fbx", "texture": { @@ -2356,7 +2356,7 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.59333323516845703, + "x": -0.65333323516845703, "y": 0.019300000742077827, "z": 0.037454843521118164 }, @@ -2381,9 +2381,9 @@ "y": 0.04787999764084816 }, "position": { - "x": -0.5103323516845703, - "y": 0.019300000742077827, - "z": -0.127054843521118164 + "x": -0.5503323516845703, + "y": 0.019300000742077827, + "z": -0.07282185554504395 }, "modelURL": "meshes/keyboard/SM_enter.fbx", "texture": { diff --git a/interface/resources/meshes/drumstick.fbx b/interface/resources/meshes/drumstick.fbx index 0243d9fd1b89f0dfe6572a7ec293a38ec571ce53..b384e6a30ae85addae191f9f9a9345e24230b08d 100644 GIT binary patch delta 17681 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 XqwX81=M(y}%V(mPt=|CVpXl))T_y31 diff --git a/interface/resources/sounds/keyboardPress.mp3 b/interface/resources/sounds/keyboardPress.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e4cd21b9715c64fbfa5153b89666c530305c0333 GIT binary patch literal 7313 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 U_tV6Ubao5I3I88j{{PN@0jrj2?*IS* literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/keyboard_key.mp3 b/interface/resources/sounds/keyboard_key.mp3 deleted file mode 100644 index e2cec81032389f719f90dd60b0ae4ca10719e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9089 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 diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 5f83c095c8..6852691634 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -45,6 +45,7 @@ #include "scripting/HMDScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" #include "raypick/StylusPointer.h" @@ -54,9 +55,9 @@ static const int LEFT_HAND_CONTROLLER_INDEX = 0; static const int RIGHT_HAND_CONTROLLER_INDEX = 1; -static const float MALLET_LENGTH = 0.2f; -static const float MALLET_TOUCH_Y_OFFSET = 0.052f; -static const float MALLET_Y_OFFSET = 0.180f; +static const float MALLET_LENGTH = 0.18f; +static const float MALLET_TOUCH_Y_OFFSET = 0.050f; +static const float MALLET_Y_OFFSET = 0.160f; static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.03f, MALLET_LENGTH, 0.03f}; @@ -65,14 +66,14 @@ static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OF static const glm::vec3 Z_AXIS {0.0f, 0.0f, 1.0f}; -static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.28f, -0.3f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.30f, -0.38f, -0.04f}; static const glm::vec3 KEYBOARD_TABLET_DEGREES_OFFSET{-45.0f, 0.0f, 0.0f}; static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_OFFSET{-0.2f, -0.27f, -0.05f}; static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET{-45.0f, 0.0f, -90.0f}; static const glm::vec3 KEYBOARD_AVATAR_OFFSET{-0.6f, 0.3f, -0.7f}; static const glm::vec3 KEYBOARD_AVATAR_DEGREES_OFFSET{0.0f, 180.0f, 0.0f}; -static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboard_key.mp3"; +static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboardPress.mp3"; static const QString MALLET_MODEL_URL = PathUtils::resourcesUrl() + "meshes/drumstick.fbx"; static const float PULSE_STRENGTH = 0.6f; @@ -221,6 +222,7 @@ Keyboard::Keyboard() { auto pointerManager = DependencyManager::get(); auto windowScriptingInterface = DependencyManager::get(); auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto hmdScriptingInterface = DependencyManager::get(); connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); @@ -228,6 +230,7 @@ Keyboard::Keyboard() { connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Keyboard::handleHoverEnd, Qt::QueuedConnection); connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); + connect(hmdScriptingInterface.data(), &HMDScriptingInterface::displayModeChanged, [&]() { setRaised(false); }); } void Keyboard::registerKeyboardHighlighting() { @@ -483,7 +486,7 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent AudioInjectorOptions audioOptions; audioOptions.localOnly = true; audioOptions.position = keyWorldPosition; - audioOptions.volume = 0.1f; + audioOptions.volume = 0.05f; AudioInjector::playSoundAndDelete(_keySound, audioOptions); @@ -835,8 +838,8 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { _textDisplay = textDisplay; _ignoreItemsLock.withWriteLock([&] { - _itemsToIgnore.push_back(_textDisplay.overlayID); - _itemsToIgnore.push_back(_anchor.overlayID); + _itemsToIgnore.append(_textDisplay.overlayID); + _itemsToIgnore.append(_anchor.overlayID); }); _layerIndex = 0; auto pointerManager = DependencyManager::get(); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 7593e12e07..22b123c85d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -535,7 +535,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay bool bestIsFront = false; bool bestIsTablet = false; auto tabletIDs = qApp->getTabletIDs(); - + const QVector keyboardKeysToDiscard = DependencyManager::get()->getKeysID(); QMutexLocker locker(&_mutex); RayToOverlayIntersectionResult result; QMapIterator i(_overlaysWorld); @@ -545,7 +545,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay auto thisOverlay = std::dynamic_pointer_cast(i.value()); if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) { + (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) || + (keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) { continue; } From 588303599185bb1adbd43da95b7a41d3db42fd15 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 14:50:03 -0800 Subject: [PATCH 45/60] Gcc doesn't like QTemporaryFile in a map so store unique ptrs --- domain-server/src/DomainServer.cpp | 5 +++-- domain-server/src/DomainServer.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 670e179f81..20ca5c94d4 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2524,10 +2524,11 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite const auto peerAddressHash = qHash(connection->socket()->peerAddress()); if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { - _pendingContentFiles.emplace(peerAddressHash, TEMPORARY_CONTENT_FILEPATH); + std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); + _pendingContentFiles[peerAddressHash] = std::move(newTemp); } - QTemporaryFile& _pendingFileContent = _pendingContentFiles[peerAddressHash]; + QTemporaryFile& _pendingFileContent = *_pendingContentFiles[peerAddressHash]; if (!_pendingFileContent.open()) { _pendingContentFiles.erase(peerAddressHash); connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 2d5729be59..5d6cd4e5f9 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -285,7 +285,7 @@ private: QHash> _pendingOAuthConnections; std::unordered_map _pendingUploadedContents; - std::unordered_map _pendingContentFiles; + std::unordered_map> _pendingContentFiles; QThread _assetClientThread; }; From 2d2cc0eaca55a14af050e622d63168872d6f6431 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 15:56:39 -0800 Subject: [PATCH 46/60] Use a random session id for a sequence of chunks --- .../resources/web/content/js/content.js | 13 +++++++---- domain-server/src/DomainServer.cpp | 22 ++++++++++--------- domain-server/src/DomainServer.h | 4 ++-- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 365c5e8403..afee2cc8a9 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -10,7 +10,7 @@ $(document).ready(function(){ function progressBarHTML(extraClass, label) { var html = "

    "; html += "
    "; - html += label + "
    "; + html += ""; return html; } @@ -24,10 +24,14 @@ $(document).ready(function(){ }); } - function uploadNextChunk(file, offset) { + function uploadNextChunk(file, offset, id) { if (offset == undefined) { offset = 0; } + if (id == undefined) { + // Identify this upload session + id = Math.round(Math.random() * 2147483647); + } var fileSize = file.size; var filename = file.name; @@ -45,6 +49,7 @@ $(document).ready(function(){ url: '/content/upload', type: 'POST', timeout: 30000, // 30 s + headers: {"X-Session-Id": id}, cache: false, processData: false, contentType: false, @@ -64,7 +69,7 @@ $(document).ready(function(){ if (!isFinal) { ajaxObject.done(function (data, textStatus, jqXHR) - { uploadNextChunk(file, offset + CHUNK_SIZE); }); + { uploadNextChunk(file, offset + CHUNK_SIZE, id); }); } else { ajaxObject.done(function(data, textStatus, jqXHR) { isRestoring = true; @@ -210,7 +215,7 @@ $(document).ready(function(){ function updateProgressBars($progressBar, value) { $progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%'); - $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "% Complete"); + $progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "%"); } function reloadBackupInformation() { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 20ca5c94d4..2a5ada729c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2519,18 +2519,21 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) { + static const QString UPLOAD_SESSION_KEY { "X-Session-Id" }; + QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY); + int sessionId = sessionIdBytes.toInt(); + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; - const auto peerAddressHash = qHash(connection->socket()->peerAddress()); - if (_pendingContentFiles.find(peerAddressHash) == _pendingContentFiles.end()) { + if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) { std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); - _pendingContentFiles[peerAddressHash] = std::move(newTemp); + _pendingContentFiles[sessionId] = std::move(newTemp); } - QTemporaryFile& _pendingFileContent = *_pendingContentFiles[peerAddressHash]; + QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId]; if (!_pendingFileContent.open()) { - _pendingContentFiles.erase(peerAddressHash); + _pendingContentFiles.erase(sessionId); connection->respond(HTTPConnection::StatusCode400); return false; } @@ -2543,16 +2546,15 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { auto deferred = makePromise("recoverFromUploadedBackup"); - deferred->then([this, peerAddressHash](QString error, QVariantMap result) { - _pendingContentFiles.erase(peerAddressHash); + deferred->then([this, sessionId](QString error, QVariantMap result) { + _pendingContentFiles.erase(sessionId); }); _contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName()); } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { - auto peerAddressHash = qHash(connection->socket()->peerAddress()); - QByteArray& _pendingUploadedContent = _pendingUploadedContents[peerAddressHash]; + QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); @@ -2560,7 +2562,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); - _pendingUploadedContents.erase(peerAddressHash); + _pendingUploadedContents.erase(sessionId); } } else { connection->respond(HTTPConnection::StatusCode400); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5d6cd4e5f9..f0c20241a2 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -284,8 +284,8 @@ private: QHash> _pendingOAuthConnections; - std::unordered_map _pendingUploadedContents; - std::unordered_map> _pendingContentFiles; + std::unordered_map _pendingUploadedContents; + std::unordered_map> _pendingContentFiles; QThread _assetClientThread; }; From 4388763c34f69dd31491fa114e78778effa8d9e0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 16 Nov 2018 16:04:40 -0800 Subject: [PATCH 47/60] fixing keyboard not being dismissing when backout of the login screen --- interface/resources/qml/dialogs/TabletLoginDialog.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index dad2bb91aa..0b19e34b0a 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -141,6 +141,7 @@ TabletModalWindow { Component.onDestruction: { loginKeyboard.raised = false; + KeyboardScriptingInterface.raised = false; } Component.onCompleted: { From 3baebf55c5d263ddcaff9edd0f43dafb4eaf3a05 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 16 Nov 2018 17:33:13 -0800 Subject: [PATCH 48/60] Don't enforce key presence in models.json --- .../octree/src/OctreeEntitiesFileParser.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 873eaff0e1..962f744c34 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -46,8 +46,13 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { bool gotId = false; bool gotVersion = false; - while (!(gotDataVersion && gotEntities && gotId && gotVersion)) { - if (nextToken() != '"') { + int token = nextToken(); + + while (true) { + if (token == '}') { + break; + } + else if (token != '"') { _errorString = "Incorrect key string"; return false; } @@ -144,15 +149,13 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { return false; } - if (gotDataVersion && gotEntities && gotId && gotVersion) { - break; - } else if (nextToken() != ',') { - _errorString = "Id/value incorrectly terminated"; - return false; + token = nextToken(); + if (token == ',') { + token = nextToken(); } } - if (nextToken() != '}' || nextToken() != -1) { + if (nextToken() != -1) { _errorString = "Ill-formed end of object"; return false; } From a0a87e71704515cd82689e7aea7d0bb83e1aa233 Mon Sep 17 00:00:00 2001 From: birarda Date: Mon, 19 Nov 2018 10:54:25 -0800 Subject: [PATCH 49/60] consider avatar traits bytes in budget and omit if over --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 7e0b6a00ad..a037f24345 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -416,7 +416,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // NOTE: Here's where we determine if we are over budget and drop remaining avatars, // or send minimal avatar data in uncommon case of PALIsOpen. int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; - bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; + auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes; + bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame; if (overBudget) { if (PALIsOpen) { _stats.overBudgetAvatars++; @@ -497,8 +498,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.avatarDataPackingElapsedTime += (quint64) chrono::duration_cast(endAvatarDataPacking - startAvatarDataPacking).count(); - // use helper to add any changed traits to our packet list - traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + if (!overBudget) { + // use helper to add any changed traits to our packet list + traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + } + remainingAvatars--; } From 84e8e21d8512b80a80d8765601200936de7da058 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 19 Nov 2018 21:08:21 +0100 Subject: [PATCH 50/60] don't trigger virtual keyboard scroll on non input focus. --- interface/resources/html/raiseAndLowerKeyboard.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index 8cdb3c2327..db8ff24fcb 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -18,7 +18,7 @@ window.isKeyboardRaised = false; window.isNumericKeyboard = false; window.isPasswordField = false; - window.lastActiveElement = null; + window.lastActiveInputElement = null; function getActiveElement() { return document.activeElement; @@ -70,11 +70,15 @@ var keyboardRaised = shouldRaiseKeyboard(); var numericKeyboard = shouldSetNumeric(); var passwordField = shouldSetPasswordField(); - var activeElement = getActiveElement(); + var activeInputElement = null; + // Only set the active input element when there is an input element focussed, otherwise it will scroll on body focus as well. + if (keyboardRaised) { + activeInputElement = getActiveElement(); + } if (isWindowFocused && (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard - || passwordField !== window.isPasswordField || activeElement !== window.lastActiveElement)) { + || passwordField !== window.isPasswordField || activeInputElement !== window.lastActiveInputElement)) { if (typeof EventBridge !== "undefined" && EventBridge !== null) { EventBridge.emitWebEvent( @@ -96,7 +100,7 @@ window.isKeyboardRaised = keyboardRaised; window.isNumericKeyboard = numericKeyboard; window.isPasswordField = passwordField; - window.lastActiveElement = activeElement; + window.lastActiveInputElement = activeInputElement; } }, POLL_FREQUENCY); From 359a841f693c4609d28b32c303418562708640cf Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 19 Nov 2018 12:20:31 -0800 Subject: [PATCH 51/60] Turn off throttling behavior when not throttling --- assignment-client/src/audio/AudioMixer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e8568a7ff3..77f416f31e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -435,7 +435,11 @@ void AudioMixer::start() { QCoreApplication::processEvents(); } - int numToRetain = nodeList->size() * (1 - _throttlingRatio); + int numToRetain = -1; + assert(_throttlingRatio >= 0.0f && _throttlingRatio <= 1.0f); + if (_throttlingRatio > EPSILON) { + numToRetain = nodeList->size() * (1.0f - _throttlingRatio); + } nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { // mix across slave threads auto mixTimer = _mixTiming.timer(); From 193b9752b80981a5a87dfda0ac05dbf4ee2ad533 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 13:50:56 -0800 Subject: [PATCH 52/60] Fix styling of properties window when narrow --- scripts/system/html/css/edit-style.css | 16 +++++++++------- scripts/system/html/js/entityProperties.js | 11 +++++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index bec926ad03..5b5c9e057c 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -590,9 +590,6 @@ div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { background-color: #373737; } -.section-header { - cursor: pointer; -} .section-header span { font-size: 30px; @@ -948,12 +945,12 @@ div.refresh input[type="button"] { } .draggable-number.left-arrow { top: -5px; - right: 106px; + left: 0px; transform: rotate(180deg); } .draggable-number.right-arrow { top: -5px; - left: 106px; + right: 0px; } .draggable-number input[type=number] { position: absolute; @@ -1123,7 +1120,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { } -body#entity-list-body { +div#grid-section, body#entity-list-body { padding-bottom: 0; margin: 16px; } @@ -1552,7 +1549,6 @@ input.rename-entity { .container { display: flex; flex-flow: row nowrap; - justify-content: space-around; margin-bottom: 8px; min-height: 28px; } @@ -1602,6 +1598,8 @@ input.rename-entity { .xyz.fstuple, .pyr.fstuple { position: relative; left: -12px; + min-width: 50px; + width: 100px; } .rgb.fstuple .tuple { @@ -1660,3 +1658,7 @@ input.number-slider { #placeholder-property-type { min-width: 0px; } + +.collapse-icon { + cursor: pointer; +} diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a4cde98abe..d3e0751732 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1158,18 +1158,21 @@ const GROUPS = [ label: "Link", type: "string", propertyID: "href", + placeholder: "URL", }, { label: "Script", type: "string", buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadScripts } ], propertyID: "script", + placeholder: "URL", }, { label: "Server Script", type: "string", buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], propertyID: "serverScripts", + placeholder: "URL", }, { label: "Server Script Status", @@ -2771,7 +2774,7 @@ function loaded() { elLegend.appendChild(createElementFromHTML(`
    ${group.label}
    `)); let elSpan = document.createElement('span'); - elSpan.className = ".collapse-icon"; + elSpan.className = "collapse-icon"; elSpan.innerText = "M"; elLegend.appendChild(elSpan); elGroup.appendChild(elLegend); @@ -3311,14 +3314,14 @@ function loaded() { getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); // Collapsible sections - let elCollapsible = document.getElementsByClassName("section-header"); + let elCollapsible = document.getElementsByClassName("collapse-icon"); let toggleCollapsedEvent = function(event) { - let element = this.parentNode; + let element = this.parentNode.parentNode; let isCollapsed = element.dataset.collapsed !== "true"; element.dataset.collapsed = isCollapsed ? "true" : false; element.setAttribute("collapsed", isCollapsed ? "true" : "false"); - element.getElementsByClassName(".collapse-icon")[0].textContent = isCollapsed ? "L" : "M"; + this.textContent = isCollapsed ? "L" : "M"; }; for (let collapseIndex = 0, numCollapsibles = elCollapsible.length; collapseIndex < numCollapsibles; ++collapseIndex) { From 7694aa8e6637cb13fc2bf1fec0c7ae00a2e506cc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 13:51:25 -0800 Subject: [PATCH 53/60] Fix draggable number going into edit too often --- scripts/system/html/js/draggableNumber.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 1f4bc21441..4d1d008f7f 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -32,7 +32,7 @@ DraggableNumber.prototype = { mouseUp: function(event) { if (event.target === this.elText && this.initialMouseEvent) { let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { + if (Math.abs(dx) <= DELTA_X_FOCUS_THRESHOLD) { this.elInput.style.visibility = "visible"; this.elText.style.visibility = "hidden"; } From 007396346c7382783446e26b1ba12031a2e917d7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Nov 2018 14:07:24 -0800 Subject: [PATCH 54/60] Fix grid tool styling --- scripts/system/html/gridControls.html | 55 ++++++++++++++------------ scripts/system/html/js/gridControls.js | 24 ++--------- 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index cd646fed51..4be002619a 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -19,47 +19,52 @@ -
    -
    - -
    -
    - +
    +
    +
    + + +
    -
    - +
    +
    + + +
    -
    +
    +
    -
    +
    +
    +
    -
    -
    +
    - -
    -
    -
    - -
    -
    -
    -
    +
    +
    -
    - +
    + +
    +
    +
    +
    +
    + +
    - +
    - \ No newline at end of file + diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index 70e91071fb..b2d5988938 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -83,44 +83,26 @@ function loaded() { var gridColor = { red: 255, green: 255, blue: 255 }; var elColor = document.getElementById("grid-color"); - var elColorRed = document.getElementById("grid-color-red"); - var elColorGreen = document.getElementById("grid-color-green"); - var elColorBlue = document.getElementById("grid-color-blue"); elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; - elColorRed.value = gridColor.red; - elColorGreen.value = gridColor.green; - elColorBlue.value = gridColor.blue; - - var colorChangeFunction = function () { - gridColor = { red: elColorRed.value, green: elColorGreen.value, blue: elColorBlue.value }; - elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")"; - emitUpdate(); - }; var colorPickFunction = function (red, green, blue) { - elColorRed.value = red; - elColorGreen.value = green; - elColorBlue.value = blue; gridColor = { red: red, green: green, blue: blue }; emitUpdate(); }; - elColorRed.addEventListener('change', colorChangeFunction); - elColorGreen.addEventListener('change', colorChangeFunction); - elColorBlue.addEventListener('change', colorChangeFunction); $('#grid-color').colpick({ colorScheme: 'dark', - layout: 'hex', + layout: 'rgbhex', color: { r: gridColor.red, g: gridColor.green, b: gridColor.blue }, + submit: false, onShow: function (colpick) { $('#grid-color').attr('active', 'true'); }, onHide: function (colpick) { $('#grid-color').attr('active', 'false'); }, - onSubmit: function (hsb, hex, rgb, el) { + onChange: function (hsb, hex, rgb, el) { $(el).css('background-color', '#' + hex); - $(el).colpickHide(); colorPickFunction(rgb.r, rgb.g, rgb.b); } }); From cd177a445703698596fdfb974d1fabd2dbf5261c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Nov 2018 15:25:14 -0800 Subject: [PATCH 55/60] catch QString exceptions that ModelCache can throw --- .../model-networking/src/model-networking/ModelCache.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e3edf7bc53..adb23ecd7d 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -243,6 +243,13 @@ void GeometryReader::run() { QMetaObject::invokeMethod(resource.data(), "finishedLoading", Q_ARG(bool, false)); } + } catch (QString& e) { + qCWarning(modelnetworking) << "Exception while loading" << _url << "--" << e; + auto resource = _resource.toStrongRef(); + if (resource) { + QMetaObject::invokeMethod(resource.data(), "finishedLoading", + Q_ARG(bool, false)); + } } } From 2a7e22bf317698f2d5d94ce011b8b07c11ef6b43 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 19 Nov 2018 15:26:08 -0800 Subject: [PATCH 56/60] Identify initial chunk of an upload so as to be more resilient --- .../resources/web/content/js/content.js | 10 ++++++++-- domain-server/src/DomainServer.cpp | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index afee2cc8a9..85bd9e68b3 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -43,7 +43,13 @@ $(document).ready(function(){ var chunk = file.slice(offset, offset + nextChunkSize, file.type); var chunkFormData = new FormData(); - var formItemName = isFinal ? 'restore-file-chunk-final' : 'restore-file-chunk'; + var formItemName = 'restore-file-chunk'; + if (offset == 0) { + formItemName = isFinal ? 'restore-file-chunk-only' : 'restore-file-chunk-initial'; + } else if (isFinal) { + formItemName = 'restore-file-chunk-final'; + } + chunkFormData.append(formItemName, chunk, filename); var ajaxParams = { url: '/content/upload', @@ -57,7 +63,7 @@ $(document).ready(function(){ }; var ajaxObject = $.ajax(ajaxParams); - ajaxObject.fail(function(jqXHR, textStatus, errorThrown) { + ajaxObject.fail(function (jqXHR, textStatus, errorThrown) { showErrorMessage( "Error", "There was a problem restoring domain content.\n" diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2a5ada729c..2d1abc8c71 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2523,12 +2523,20 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY); int sessionId = sessionIdBytes.toInt(); + bool newUpload = itemName == "restore-file" || itemName == "restore-file-chunk-initial" || itemName == "restore-file-chunk-only"; + if (filename.endsWith(".zip", Qt::CaseInsensitive)) { static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" }; if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) { + if (!newUpload) { + return false; + } std::unique_ptr newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH)); _pendingContentFiles[sessionId] = std::move(newTemp); + } else if (newUpload) { + qCDebug(domain_server) << "New upload received using existing session ID"; + _pendingContentFiles[sessionId]->resize(0); } QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId]; @@ -2543,7 +2551,7 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite // Respond immediately - will timeout if we wait for restore. connection->respond(HTTPConnection::StatusCode200); - if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") { auto deferred = makePromise("recoverFromUploadedBackup"); deferred->then([this, sessionId](QString error, QVariantMap result) { @@ -2554,11 +2562,15 @@ bool DomainServer::processPendingContent(HTTPConnection* connection, QString ite } } else if (filename.endsWith(".json", Qt::CaseInsensitive) || filename.endsWith(".json.gz", Qt::CaseInsensitive)) { + if (_pendingUploadedContents.find(sessionId) == _pendingUploadedContents.end() && !newUpload) { + qCDebug(domain_server) << "Json upload with invalid session ID received"; + return false; + } QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId]; _pendingUploadedContent += dataChunk; connection->respond(HTTPConnection::StatusCode200); - if (itemName == "restore-file-chunk-final" || itemName == "restore-file") { + if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") { // invoke our method to hand the new octree file off to the octree server QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent)); From 26a50e182045115417e3921937822cde3c0e7a67 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Nov 2018 15:38:12 -0800 Subject: [PATCH 57/60] don't log urls! --- libraries/model-networking/src/model-networking/ModelCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index adb23ecd7d..d3f24073c4 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -244,7 +244,7 @@ void GeometryReader::run() { Q_ARG(bool, false)); } } catch (QString& e) { - qCWarning(modelnetworking) << "Exception while loading" << _url << "--" << e; + qCWarning(modelnetworking) << "Exception while loading model --" << e; auto resource = _resource.toStrongRef(); if (resource) { QMetaObject::invokeMethod(resource.data(), "finishedLoading", From ff6d43d0e4890d8612bb0d177064ad2e3dfdb5e2 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:15:11 -0800 Subject: [PATCH 58/60] block updates during dragging --- scripts/system/edit.js | 9 ++ scripts/system/html/js/draggableNumber.js | 57 +++++-- scripts/system/html/js/entityProperties.js | 166 +++++++++++++-------- 3 files changed, 150 insertions(+), 82 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 48fdb8e565..85fd8c536d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2223,6 +2223,7 @@ var PropertiesTool = function (opts) { // are selected or if no entity is selected this will be `null`. var currentSelectedEntityID = null; var statusMonitor = null; + var nextPropertyUpdateDisabled = false; that.setVisible = function (newVisible) { visible = newVisible; @@ -2260,6 +2261,11 @@ var PropertiesTool = function (opts) { }; function updateSelections(selectionUpdated) { + if (nextPropertyUpdateDisabled) { + nextPropertyUpdateDisabled = false; + return; + } + var data = { type: 'update', spaceMode: selectionDisplay.getSpaceMode() @@ -2356,6 +2362,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); + nextPropertyUpdateDisabled = data.blockUpdateCallback === true; selectionManager._update(false, this); } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { //the event bridge and json parsing handle our avatar id string differently. @@ -2466,6 +2473,8 @@ var PropertiesTool = function (opts) { tooltips: Script.require('./assets/data/createAppTooltips.json'), hmdActive: HMD.active, }); + } else if (data.type === "updateProperties") { + updateSelections(true); } }; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 1f4bc21441..c08cac2ce4 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -8,11 +8,14 @@ const DELTA_X_FOCUS_THRESHOLD = 1; -function DraggableNumber(min, max, step, decimals) { +function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; this.decimals = decimals; + this.dragStartFunction = dragStart; + this.dragEndFunction = dragEnd; + this.dragging = false; this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; @@ -32,7 +35,7 @@ DraggableNumber.prototype = { mouseUp: function(event) { if (event.target === this.elText && this.initialMouseEvent) { let dx = event.clientX - this.initialMouseEvent.clientX; - if (dx <= DELTA_X_FOCUS_THRESHOLD) { + if (Math.abs(dx) <= DELTA_X_FOCUS_THRESHOLD) { this.elInput.style.visibility = "visible"; this.elText.style.visibility = "hidden"; } @@ -41,22 +44,32 @@ DraggableNumber.prototype = { }, documentMouseMove: function(event) { - if (this.lastMouseEvent) { - let initialValue = this.elInput.value; - let dx = event.clientX - this.lastMouseEvent.clientX; - let changeValue = dx !== 0; - if (changeValue) { - while (dx !== 0) { - if (dx > 0) { - this.stepUp(); - --dx; - } else { - this.stepDown(); - ++dx; + if (this.initialMouseEvent) { + let dxFromInitial = event.clientX - this.initialMouseEvent.clientX; + if (Math.abs(dxFromInitial) > DELTA_X_FOCUS_THRESHOLD && this.lastMouseEvent) { + let initialValue = this.elInput.value; + let dx = event.clientX - this.lastMouseEvent.clientX; + let changeValue = dx !== 0; + if (changeValue) { + while (dx !== 0) { + if (dx > 0) { + this.elInput.stepUp(); + --dx; + } else { + this.elInput.stepDown(); + ++dx; + } + } + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); } } - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (!this.dragging) { + if (this.dragStartFunction) { + this.dragStartFunction(); + } + this.dragging = true; } } this.lastMouseEvent = event; @@ -64,6 +77,12 @@ DraggableNumber.prototype = { }, documentMouseUp: function(event) { + if (this.dragging) { + if (this.dragEndFunction) { + this.dragEndFunction(); + } + this.dragging = false; + } this.lastMouseEvent = null; document.removeEventListener("mousemove", this.onDocumentMouseMove); document.removeEventListener("mouseup", this.onDocumentMouseUp); @@ -72,11 +91,17 @@ DraggableNumber.prototype = { stepUp: function() { this.elInput.stepUp(); this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } }, stepDown: function() { this.elInput.stepDown(); this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } }, setValue: function(newValue) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c49e0d88f6..850c012be0 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1636,7 +1636,7 @@ function updateVisibleSpaceModeProperties() { * PROPERTY UPDATE FUNCTIONS */ -function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { +function updateProperty(originalPropertyName, propertyValue, isParticleProperty, blockUpdateCallback) { let propertyUpdate = {}; // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times let splitPropertyName = originalPropertyName.split('.'); @@ -1662,7 +1662,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) }); particleSyncDebounce(); } else { - updateProperties(propertyUpdate); + updateProperties(propertyUpdate, blockUpdateCallback); } } @@ -1671,66 +1671,89 @@ var particleSyncDebounce = _.debounce(function () { particlePropertyUpdates = {}; }, DEBOUNCE_TIMEOUT); -function updateProperties(propertiesToUpdate) { +function updateProperties(propertiesToUpdate, blockUpdateCallback) { + if (blockUpdateCallback === undefined) { + blockUpdateCallback = false; + } EventBridge.emitWebEvent(JSON.stringify({ id: lastEntityID, type: "update", - properties: propertiesToUpdate + properties: propertiesToUpdate, + blockUpdateCallback: blockUpdateCallback })); } -function createEmitTextPropertyUpdateFunction(propertyName, isParticleProperty) { +function createEmitTextPropertyUpdateFunction(property) { return function() { - updateProperty(propertyName, this.value, isParticleProperty); + updateProperty(property.name, this.value, property.isParticleProperty); }; } -function createEmitCheckedPropertyUpdateFunction(propertyName, inverse, isParticleProperty) { +function createEmitCheckedPropertyUpdateFunction(property) { return function() { - updateProperty(propertyName, inverse ? !this.checked : this.checked, isParticleProperty); + updateProperty(property.name, property.data.inverse ? !this.checked : this.checked, property.isParticleProperty); }; } -function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, isParticleProperty) { +function createDragStartFunction(property) { return function() { + property.dragging = true; + }; +} + +function createDragEndFunction(property) { + return function() { + property.dragging = false; + EventBridge.emitWebEvent(JSON.stringify({ + type: "updateProperties" + })); + }; +} + +function createEmitNumberPropertyUpdateFunction(property) { + return function() { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let value = parseFloat(this.value) * multiplier; - updateProperty(propertyName, value, isParticleProperty); + updateProperty(property.name, value, property.isParticleProperty, property.dragging); }; } -function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier, isParticleProperty) { +function createEmitVec2PropertyUpdateFunction(property) { return function () { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let newValue = { - x: elX.value * multiplier, - y: elY.value * multiplier + x: property.elNumberX.elInput.value * multiplier, + y: property.elNumberY.elInput.value * multiplier }; - updateProperty(propertyName, newValue, isParticleProperty); + updateProperty(property.name, newValue, property.isParticleProperty, property.dragging); }; } -function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier, isParticleProperty) { +function createEmitVec3PropertyUpdateFunction(property) { return function() { + let multiplier = property.data.multiplier; if (multiplier === undefined) { multiplier = 1; } let newValue = { - x: elX.value * multiplier, - y: elY.value * multiplier, - z: elZ.value * multiplier + x: property.elNumberX.elInput.value * multiplier, + y: property.elNumberY.elInput.value * multiplier, + z: property.elNumberZ.elInput.value * multiplier }; - updateProperty(propertyName, newValue, isParticleProperty); + updateProperty(property.name, newValue, property.isParticleProperty, property.dragging); }; } -function createEmitColorPropertyUpdateFunction(propertyName, elRed, elGreen, elBlue, isParticleProperty) { +function createEmitColorPropertyUpdateFunction(property) { return function() { - emitColorPropertyUpdate(propertyName, elRed.value, elGreen.value, elBlue.value, isParticleProperty); + emitColorPropertyUpdate(property.name, property.elNumberR.elInput.value, property.elNumberG.elInput.value, + property.elNumberB.elInput.value, property.isParticleProperty); }; } @@ -1755,10 +1778,10 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen updateProperty(propertyName, propertyValue, isParticleProperty); } -function createImageURLUpdateFunction(propertyName, isParticleProperty) { +function createImageURLUpdateFunction(property) { return function () { let newTextures = JSON.stringify({ "tex.picture": this.value }); - updateProperty(propertyName, newTextures, isParticleProperty); + updateProperty(property.name, newTextures, property.isParticleProperty); }; } @@ -1768,7 +1791,6 @@ function createImageURLUpdateFunction(propertyName, isParticleProperty) { */ function createStringProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -1782,7 +1804,7 @@ function createStringProperty(property, elProperty) { `) - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -1821,30 +1843,30 @@ function createBoolProperty(property, elProperty) { elInput, propertyName, property.isParticleProperty); }); } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(propertyName, propertyData.inverse, - property.isParticleProperty)); + elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(property)); } return elInput; } function createNumberProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; elProperty.className = "draggable-number"; - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, - propertyData.step, propertyData.decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); let defaultValue = propertyData.defaultValue; if (defaultValue !== undefined) { elDraggableNumber.elInput.value = defaultValue; } - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(propertyName, propertyData.multiplier, - property.isParticleProperty); + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); elDraggableNumber.elInput.setAttribute("id", elementID); @@ -1858,22 +1880,18 @@ function createNumberProperty(property, elProperty) { } function createVec3Property(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; let propertyData = property.data; elProperty.className = propertyData.vec3Type + " fstuple"; - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberZ = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); + let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); + let elNumberZ = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER]); + elProperty.appendChild(elNumberX.elDiv); + elProperty.appendChild(elNumberY.elDiv); + elProperty.appendChild(elNumberZ.elDiv); - let valueChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, - elNumberZ.elInput, propertyData.multiplier, - property.isParticleProperty); + let valueChangeFunction = createEmitVec3PropertyUpdateFunction(property); elNumberX.setValueChangeFunction(valueChangeFunction); elNumberY.setValueChangeFunction(valueChangeFunction); elNumberZ.setValueChangeFunction(valueChangeFunction); @@ -1886,8 +1904,6 @@ function createVec3Property(property, elProperty) { } function createVec2Property(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; let propertyData = property.data; elProperty.className = propertyData.vec2Type + " fstuple"; @@ -1897,13 +1913,15 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let elNumberX = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); - let elNumberY = createTupleNumberInput(elProperty, elementID, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER], - propertyData.min, propertyData.max, propertyData.step, propertyData.decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); - let valueChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elNumberX.elInput, elNumberY.elInput, - propertyData.multiplier, property.isParticleProperty); + let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); + let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); + elProperty.appendChild(elNumberX.elDiv); + elProperty.appendChild(elNumberY.elDiv); + + let valueChangeFunction = createEmitVec2PropertyUpdateFunction(property); elNumberX.setValueChangeFunction(valueChangeFunction); elNumberY.setValueChangeFunction(valueChangeFunction); @@ -1916,6 +1934,7 @@ function createVec2Property(property, elProperty) { function createColorProperty(property, elProperty) { let propertyName = property.name; let elementID = property.elementID; + let propertyData = property.data; elProperty.className = "rgb fstuple"; @@ -1929,12 +1948,24 @@ function createColorProperty(property, elProperty) { elProperty.appendChild(elColorPicker); elProperty.appendChild(elTuple); - let elNumberR = createTupleNumberInput(elTuple, elementID, "red", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elNumberG = createTupleNumberInput(elTuple, elementID, "green", COLOR_MIN, COLOR_MAX, COLOR_STEP); - let elNumberB = createTupleNumberInput(elTuple, elementID, "blue", COLOR_MIN, COLOR_MAX, COLOR_STEP); + if (propertyData.min === undefined) { + propertyData.min = COLOR_MIN; + } + if (propertyData.max === undefined) { + propertyData.max = COLOR_MAX; + } + if (propertyData.step === undefined) { + propertyData.step = COLOR_STEP; + } - let valueChangeFunction = createEmitColorPropertyUpdateFunction(propertyName, elNumberR.elInput, elNumberG.elInput, - elNumberB.elInput, property.isParticleProperty); + let elNumberR = createTupleNumberInput(property, "red"); + let elNumberG = createTupleNumberInput(property, "green"); + let elNumberB = createTupleNumberInput(property, "blue"); + elTuple.appendChild(elNumberR.elDiv); + elTuple.appendChild(elNumberG.elDiv); + elTuple.appendChild(elNumberB.elDiv); + + let valueChangeFunction = createEmitColorPropertyUpdateFunction(property); elNumberR.setValueChangeFunction(valueChangeFunction); elNumberG.setValueChangeFunction(valueChangeFunction); elNumberB.setValueChangeFunction(valueChangeFunction); @@ -1973,7 +2004,6 @@ function createColorProperty(property, elProperty) { } function createDropdownProperty(property, propertyID, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -1990,7 +2020,7 @@ function createDropdownProperty(property, propertyID, elProperty) { elInput.add(option); } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -1998,7 +2028,6 @@ function createDropdownProperty(property, propertyID, elProperty) { } function createTextareaProperty(property, elProperty) { - let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; @@ -2010,7 +2039,7 @@ function createTextareaProperty(property, elProperty) { elInput.readOnly = true; } - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(propertyName, property.isParticleProperty)); + elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elProperty.appendChild(elInput); @@ -2102,7 +2131,10 @@ function createButtonsProperty(property, elProperty, elLabel) { return elProperty; } -function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, step, decimals) { +function createTupleNumberInput(property, subLabel) { + let propertyElementID = property.elementID; + let propertyData = property.data; + let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); @@ -2111,11 +2143,13 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, elLabel.setAttribute("for", elementID); elLabel.style.visibility = "visible"; - let elDraggableNumber = new DraggableNumber(min, max, step, decimals); + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); elDraggableNumber.elInput.setAttribute("id", elementID); elDraggableNumber.elDiv.className += " fstuple"; elDraggableNumber.elText.insertBefore(elLabel, elDraggableNumber.elLeftArrow); - elTuple.appendChild(elDraggableNumber.elDiv); return elDraggableNumber; } @@ -2388,7 +2422,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r userDataElement.value = propertyUpdate.userData; - updateProperties(propertyUpdate); + updateProperties(propertyUpdate, false); } var editor = null; @@ -3306,7 +3340,7 @@ function loaded() { } }); - getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures', false)); + getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction(properties['textures'])); // Collapsible sections let elCollapsible = document.getElementsByClassName("section-header"); @@ -3401,7 +3435,7 @@ function loaded() { let propertyID = elDropdown.getAttribute("propertyID"); let property = properties[propertyID]; property.elInput = dt; - dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name, property.isParticleProperty)); + dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); } elDropdowns = document.getElementsByTagName("select"); From 249ebb238923d174481ac11b3c2e139da84152c8 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:21:52 -0800 Subject: [PATCH 59/60] remove unneeded --- scripts/system/html/js/entityProperties.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 850c012be0..eca38f099a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1857,7 +1857,6 @@ function createNumberProperty(property, elProperty) { let dragStartFunction = createDragStartFunction(property); let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, propertyData.decimals, dragStartFunction, dragEndFunction); @@ -1913,9 +1912,6 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elTuple); - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); elProperty.appendChild(elNumberX.elDiv); @@ -2134,7 +2130,6 @@ function createButtonsProperty(property, elProperty, elLabel) { function createTupleNumberInput(property, subLabel) { let propertyElementID = property.elementID; let propertyData = property.data; - let elementID = propertyElementID + "-" + subLabel.toLowerCase(); let elLabel = document.createElement('label'); From dff4e34e7bc96eb622c0d2b4733e0e85b7cb3878 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 19 Nov 2018 16:59:33 -0800 Subject: [PATCH 60/60] CR changes --- scripts/system/edit.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 85fd8c536d..077d50ddde 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2223,7 +2223,7 @@ var PropertiesTool = function (opts) { // are selected or if no entity is selected this will be `null`. var currentSelectedEntityID = null; var statusMonitor = null; - var nextPropertyUpdateDisabled = false; + var blockPropertyUpdates = false; that.setVisible = function (newVisible) { visible = newVisible; @@ -2261,8 +2261,7 @@ var PropertiesTool = function (opts) { }; function updateSelections(selectionUpdated) { - if (nextPropertyUpdateDisabled) { - nextPropertyUpdateDisabled = false; + if (blockPropertyUpdates) { return; } @@ -2362,8 +2361,9 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - nextPropertyUpdateDisabled = data.blockUpdateCallback === true; + blockPropertyUpdates = data.blockUpdateCallback === true; selectionManager._update(false, this); + blockPropertyUpdates = false; } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { //the event bridge and json parsing handle our avatar id string differently. var actualID = data.id.split('"')[1];