From 7987595dcd2bf66abcf843d2c4ff0a2efa892b18 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 8 Oct 2015 11:32:10 -0700 Subject: [PATCH 01/57] set the bearer poll timeout for wlan scan in Qt 5.5 --- assignment-client/src/AssignmentClientApp.cpp | 4 ++++ domain-server/src/DomainServer.cpp | 4 ++++ interface/src/Application.cpp | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 3b9f8af868..cddce5de7d 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -27,6 +27,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { + // to work around the Qt constant wireless scanning, set the env for polling interval very high + const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); + qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); + # ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); # endif diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 48bb9bed62..b5fd9f2b20 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -62,6 +62,10 @@ DomainServer::DomainServer(int argc, char* argv[]) : LogUtils::init(); Setting::init(); + + // to work around the Qt constant wireless scanning, set the env for polling interval very high + const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); + qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c427379f74..e6b158d37e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -364,7 +364,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : thread()->setObjectName("Main Thread"); setInstance(this); - + + // to work around the Qt constant wireless scanning, set the env for polling interval very high + const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); + qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); + _entityClipboard->createRootElement(); _pluginContainer = new PluginContainerProxy(); From 7d3b300037548f26ff8e55cf82d1622fa7f68124 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 11 Oct 2015 14:51:12 +0200 Subject: [PATCH 02/57] Fix set avatar rotations from scripts (different tread) --- interface/src/avatar/MyAvatar.cpp | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..de52485407 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1154,32 +1154,41 @@ void MyAvatar::setJointTranslations(QVector jointTranslations) { } void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); } void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); } void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); } void MyAvatar::clearJointData(int index) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); - _rig->clearJointAnimationPriority(index); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); + return; } + // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); + _rig->clearJointAnimationPriority(index); } void MyAvatar::clearJointsData() { From ab32470699bf005e044de8d672cd8540b816481b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 09:12:33 -0700 Subject: [PATCH 03/57] Fixed syntax error in hiddenEntityResetScript --- unpublishedScripts/hiddenEntityReset.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index d480c0ac74..10966f3a7b 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -407,11 +407,6 @@ type: 'Box', position: startPosition, dimensions: TARGET_DIMENSIONS, - color: { - red: 0, - green: 255, - blue: 0 - }, rotation: rotation, visible: false, collisionsWillMove: false, @@ -734,7 +729,6 @@ - var sconceLight3 = Entities.addEntity({ type: "Light", position: { @@ -888,7 +882,7 @@ y: 1.13, z: 0.2 }, - rotation: rotation2, + rotation: rotation, collisionsWillMove: true, gravity: { x: 0, @@ -1256,7 +1250,7 @@ y: 0.05, z: 0.25 } - },]; + }, ]; var modelURL, entity; for (i = 0; i < blockTypes.length; i++) { @@ -1305,7 +1299,6 @@ Script.scriptEnding.connect(cleanup); } }; - // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); +}); \ No newline at end of file From 08a94189b73fa64a06746e526fc8de6104eba817 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 09:31:12 -0700 Subject: [PATCH 04/57] explicitly setting isSpotlight to false --- unpublishedScripts/hiddenEntityReset.js | 6 +++++- unpublishedScripts/masterReset.js | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 10966f3a7b..0a678d668e 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -52,7 +52,6 @@ }; - MasterReset = function() { var resetKey = "resetMe"; var GRABBABLE_DATA_KEY = "grabbableKey"; @@ -653,6 +652,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -680,6 +680,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -749,6 +750,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -777,6 +779,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -804,6 +807,7 @@ green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 693e2eb28b..d9199a124d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -625,6 +625,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -652,6 +653,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -701,7 +703,6 @@ MasterReset = function() { - var sconceLight3 = Entities.addEntity({ type: "Light", position: { @@ -722,6 +723,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -750,6 +752,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -777,6 +780,7 @@ MasterReset = function() { green: 146, blue: 24 }, + isSpotlight: false, userData: JSON.stringify({ resetMe: { resetMe: true, @@ -1223,7 +1227,7 @@ MasterReset = function() { y: 0.05, z: 0.25 } - },]; + }, ]; var modelURL, entity; for (i = 0; i < blockTypes.length; i++) { @@ -1271,4 +1275,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; +}; \ No newline at end of file From a6da91ee35e5a76a9c89089207e6f3016173e1c5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 12 Oct 2015 19:03:34 +0200 Subject: [PATCH 05/57] Fix walk.js script --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 8b99ad2a98..5b42a8fb78 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointData(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 8d54fecc92..02bc23a2e2 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointData(jointName, Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file From 03eaa9525836518c854f49b1d77eb763bfa609fe Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 8 Oct 2015 16:39:25 -0700 Subject: [PATCH 06/57] initial hip translation from IK works for 2D 3rd person but probably not well for HMD --- .../defaultAvatar_full/avatar-animation.json | 12 ++++ .../animation/src/AnimInverseKinematics.cpp | 62 +++++++++++++++++-- .../animation/src/AnimInverseKinematics.h | 11 +++- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index dceecddfe0..e930f583f6 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -23,6 +23,18 @@ "positionVar": "leftHandPosition", "rotationVar": "leftHandRotation" }, + { + "jointName": "RightFoot", + "positionVar": "rightToePosition", + "rotationVar": "rightToeRotation", + "typeVar": "rightToeType" + }, + { + "jointName": "LeftFoot", + "positionVar": "leftToePosition", + "rotationVar": "leftToeRotation", + "typeVar": "leftToeType" + }, { "jointName": "Neck", "positionVar": "neckPosition", diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 8a9971f362..54ec2d839b 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -343,7 +343,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // build a list of targets from _targetVarVec std::vector targets; computeTargets(animVars, targets, underPoses); - + if (targets.empty()) { // no IK targets but still need to enforce constraints std::map::iterator constraintItr = _constraints.begin(); @@ -355,7 +355,45 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars ++constraintItr; } } else { + // shift the hips according to the offset from the previous frame + float offsetLength = glm::length(_actualHipsOffset); + const float MIN_OFFSET_LENGTH = 0.03f; + if (offsetLength > MIN_OFFSET_LENGTH) { + // but only if actual offset is long enough + float scaleFactor = ((offsetLength - MIN_OFFSET_LENGTH) / offsetLength); + _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _actualHipsOffset; + } + solveWithCyclicCoordinateDescent(targets); + + // compute the new target hips offset (for next frame) + // by looking for discrepancies between where a targeted endEffector is + // and where it wants to be (after IK solutions are done) + _targetHipsOffset = Vectors::ZERO; + for (auto& target: targets) { + if (target.index == _headIndex && _headIndex != -1) { + // special handling for headTarget + AnimPose headUnderPose = _skeleton->getAbsolutePose(_headIndex, underPoses); + AnimPose headOverPose = headUnderPose; + if (target.type == IKTarget::Type::RotationOnly) { + headOverPose = _skeleton->getAbsolutePose(_headIndex, _relativePoses); + } else if (target.type == IKTarget::Type::RotationAndPosition) { + headOverPose = target.pose; + } + glm::vec3 headOffset = headOverPose.trans - headUnderPose.trans; + const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; + _targetHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * headOffset; + } else if (target.type == IKTarget::Type::RotationAndPosition) { + glm::vec3 actualPosition = _skeleton->getAbsolutePose(target.index, _relativePoses).trans; + glm::vec3 targetPosition = target.pose.trans; + _targetHipsOffset += targetPosition - actualPosition; + } + } + + // smooth transitions by relaxing targetHipsOffset onto actualHipsOffset over some timescale + const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; + glm::vec3 deltaOffset = (_targetHipsOffset - _actualHipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); + _actualHipsOffset += deltaOffset; } } return _relativePoses; @@ -477,7 +515,7 @@ void AnimInverseKinematics::initConstraints() { stConstraint->setSwingLimits(minDots); constraint = static_cast(stConstraint); - } else if (0 == baseName.compare("UpLegXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("UpLeg", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); @@ -581,7 +619,7 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_NECK_TWIST = PI / 2.0f; + const float MAX_NECK_TWIST = PI / 4.0f; stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); std::vector minDots; @@ -589,6 +627,18 @@ void AnimInverseKinematics::initConstraints() { minDots.push_back(cosf(MAX_NECK_SWING)); stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); + } else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) { + SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); + stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); + const float MAX_HEAD_TWIST = PI / 4.0f; + stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST); + + std::vector minDots; + const float MAX_HEAD_SWING = PI / 4.0f; + minDots.push_back(cosf(MAX_HEAD_SWING)); + stConstraint->setSwingLimits(minDots); + constraint = static_cast(stConstraint); } else if (0 == baseName.compare("ForeArm", Qt::CaseInsensitive)) { // The elbow joint rotates about the parent-frame's zAxis (-zAxis) for the Right (Left) arm. @@ -621,7 +671,7 @@ void AnimInverseKinematics::initConstraints() { eConstraint->setAngleLimits(minAngle, maxAngle); constraint = static_cast(eConstraint); - } else if (0 == baseName.compare("LegXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("Leg", Qt::CaseInsensitive)) { // The knee joint rotates about the parent-frame's -xAxis. ElbowConstraint* eConstraint = new ElbowConstraint(); glm::quat referenceRotation = _defaultRelativePoses[i].rot; @@ -652,7 +702,7 @@ void AnimInverseKinematics::initConstraints() { eConstraint->setAngleLimits(minAngle, maxAngle); constraint = static_cast(eConstraint); - } else if (0 == baseName.compare("FootXXX", Qt::CaseInsensitive)) { + } else if (0 == baseName.compare("Foot", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); @@ -697,7 +747,9 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (skeleton) { initConstraints(); + _headIndex = _skeleton->nameToJointIndex("Head"); } else { clearConstraints(); + _headIndex = -1; } } diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 0ef81f8b3c..634c71cea9 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -60,6 +60,10 @@ protected: void clearConstraints(); void initConstraints(); + // no copies + AnimInverseKinematics(const AnimInverseKinematics&) = delete; + AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; + struct IKTargetVar { IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, @@ -85,9 +89,10 @@ protected: AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _relativePoses; // current relative poses - // no copies - AnimInverseKinematics(const AnimInverseKinematics&) = delete; - AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; + // experimental data for moving hips during IK + int _headIndex = -1; + glm::vec3 _targetHipsOffset = Vectors::ZERO; // offset we want + glm::vec3 _actualHipsOffset = Vectors::ZERO; // offset we have // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm From 56f038d5a7409e16dd4c97a7ed39a830c824b0b8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 10 Oct 2015 10:13:33 -0700 Subject: [PATCH 07/57] simpler logic for tracking hips offset --- .../animation/src/AnimInverseKinematics.cpp | 16 ++++++++-------- libraries/animation/src/AnimInverseKinematics.h | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 54ec2d839b..247a7c8409 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -356,12 +356,12 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } } else { // shift the hips according to the offset from the previous frame - float offsetLength = glm::length(_actualHipsOffset); + float offsetLength = glm::length(_hipsOffset); const float MIN_OFFSET_LENGTH = 0.03f; if (offsetLength > MIN_OFFSET_LENGTH) { // but only if actual offset is long enough float scaleFactor = ((offsetLength - MIN_OFFSET_LENGTH) / offsetLength); - _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _actualHipsOffset; + _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _hipsOffset; } solveWithCyclicCoordinateDescent(targets); @@ -369,7 +369,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // compute the new target hips offset (for next frame) // by looking for discrepancies between where a targeted endEffector is // and where it wants to be (after IK solutions are done) - _targetHipsOffset = Vectors::ZERO; + glm::vec3 newOffset = Vectors::ZERO; for (auto& target: targets) { if (target.index == _headIndex && _headIndex != -1) { // special handling for headTarget @@ -382,18 +382,18 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } glm::vec3 headOffset = headOverPose.trans - headUnderPose.trans; const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; - _targetHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * headOffset; + newOffset += HEAD_OFFSET_SLAVE_FACTOR * headOffset; } else if (target.type == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(target.index, _relativePoses).trans; glm::vec3 targetPosition = target.pose.trans; - _targetHipsOffset += targetPosition - actualPosition; + newOffset += targetPosition - actualPosition; } } - // smooth transitions by relaxing targetHipsOffset onto actualHipsOffset over some timescale + // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; - glm::vec3 deltaOffset = (_targetHipsOffset - _actualHipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); - _actualHipsOffset += deltaOffset; + glm::vec3 deltaOffset = (newOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); + _hipsOffset += deltaOffset; } } return _relativePoses; diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 634c71cea9..8fc03b7a0a 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -91,8 +91,7 @@ protected: // experimental data for moving hips during IK int _headIndex = -1; - glm::vec3 _targetHipsOffset = Vectors::ZERO; // offset we want - glm::vec3 _actualHipsOffset = Vectors::ZERO; // offset we have + glm::vec3 _hipsOffset = Vectors::ZERO; // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm From bc48f70877190f4542a53006d4671745ee3fbb99 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 10 Oct 2015 11:15:51 -0700 Subject: [PATCH 08/57] move IKTarget into its own files --- .../animation/src/AnimInverseKinematics.cpp | 66 +++++++++---------- .../animation/src/AnimInverseKinematics.h | 13 +--- libraries/animation/src/IKTarget.cpp | 31 +++++++++ libraries/animation/src/IKTarget.h | 42 ++++++++++++ libraries/animation/src/Rig.cpp | 11 ++-- 5 files changed, 112 insertions(+), 51 deletions(-) create mode 100644 libraries/animation/src/IKTarget.cpp create mode 100644 libraries/animation/src/IKTarget.h diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 247a7c8409..00a752c69b 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -96,13 +96,13 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } else { IKTarget target; AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); - target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); - target.setType(animVars.lookup(targetVar.typeVar, QString(""))); - target.index = targetVar.jointIndex; + target.setPose(animVars.lookup(targetVar.rotationVar, defaultPose.rot), + animVars.lookup(targetVar.positionVar, defaultPose.trans)); + target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::Unknown)); + target.setIndex(targetVar.jointIndex); targets.push_back(target); - if (target.index > _maxTargetIndex) { - _maxTargetIndex = target.index; + if (targetVar.jointIndex > _maxTargetIndex) { + _maxTargetIndex = targetVar.jointIndex; } } } @@ -141,14 +141,13 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; diff --git a/libraries/animation/src/IKTarget.cpp b/libraries/animation/src/IKTarget.cpp new file mode 100644 index 0000000000..a7e3296d35 --- /dev/null +++ b/libraries/animation/src/IKTarget.cpp @@ -0,0 +1,31 @@ +// +// IKTarget.cpp +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "IKTarget.h" + +void IKTarget::setPose(const glm::quat& rotation, const glm::vec3& translation) { + _pose.rot = rotation; + _pose.trans = translation; +} + +void IKTarget::setType(int type) { + switch (type) { + case (int)Type::RotationAndPosition: + _type = Type::RotationAndPosition; + break; + case (int)Type::RotationOnly: + _type = Type::RotationOnly; + break; + case (int)Type::HmdHead: + _type = Type::HmdHead; + break; + default: + _type = Type::Unknown; + } +} diff --git a/libraries/animation/src/IKTarget.h b/libraries/animation/src/IKTarget.h new file mode 100644 index 0000000000..fa920d1511 --- /dev/null +++ b/libraries/animation/src/IKTarget.h @@ -0,0 +1,42 @@ +// +// IKTarget.h +// +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_IKTarget_h +#define hifi_IKTarget_h + +#include "AnimSkeleton.h" + +class IKTarget { +public: + enum class Type { + RotationAndPosition, + RotationOnly, + HmdHead, + Unknown, + }; + + IKTarget() {} + + const glm::vec3& getTranslation() const { return _pose.trans; } + const glm::quat& getRotation() const { return _pose.rot; } + int getIndex() const { return _index; } + Type getType() const { return _type; } + + void setPose(const glm::quat& rotation, const glm::vec3& translation); + void setIndex(int index) { _index = index; } + void setType(int); + +private: + AnimPose _pose; + int _index = -1; + Type _type = Type::RotationAndPosition; + +}; + +#endif // hifi_IKTarget_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7f300fc84a..3a1dafa10c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -14,13 +14,14 @@ #include #include -#include "NumericalConstants.h" +#include +#include + #include "AnimationHandle.h" #include "AnimationLogging.h" #include "AnimSkeleton.h" -#include "DebugDraw.h" +#include "IKTarget.h" -#include "Rig.h" void Rig::HeadParameters::dump() const { qCDebug(animation, "HeadParameters ="); @@ -1057,7 +1058,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); - _animVars.set("headAndNeckType", QString("RotationAndPosition")); + _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); @@ -1070,7 +1071,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("headPosition"); _animVars.set("headRotation", realLocalHeadOrientation); - _animVars.set("headAndNeckType", QString("RotationOnly")); + _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); _animVars.unset("neckRotation"); } From 6a96d5f0c5d9b52a7e4c84a2555c0788637e4f9b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 10 Oct 2015 14:37:19 -0700 Subject: [PATCH 09/57] don't bother to create targets with bad types --- .../animation/src/AnimInverseKinematics.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 00a752c69b..c9f0960a4f 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -95,14 +95,16 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } else { IKTarget target; - AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - target.setPose(animVars.lookup(targetVar.rotationVar, defaultPose.rot), - animVars.lookup(targetVar.positionVar, defaultPose.trans)); - target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::Unknown)); - target.setIndex(targetVar.jointIndex); - targets.push_back(target); - if (targetVar.jointIndex > _maxTargetIndex) { - _maxTargetIndex = targetVar.jointIndex; + target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); + if (target.getType() != IKTarget::Type::Unknown) { + AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); + target.setPose(animVars.lookup(targetVar.rotationVar, defaultPose.rot), + animVars.lookup(targetVar.positionVar, defaultPose.trans)); + target.setIndex(targetVar.jointIndex); + targets.push_back(target); + if (targetVar.jointIndex > _maxTargetIndex) { + _maxTargetIndex = targetVar.jointIndex; + } } } } From 5f1068c404facaf50389606946413b2b75265b2b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 10 Oct 2015 15:53:03 -0700 Subject: [PATCH 10/57] cleanup and optimization of IK loop --- .../animation/src/AnimInverseKinematics.cpp | 183 +++++++++--------- 1 file changed, 90 insertions(+), 93 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index c9f0960a4f..0d1868d955 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -143,106 +143,114 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); + if (pivotIndex == -1) { + continue; + } + int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); + if (pivotsParentIndex == -1) { + // TODO?: handle case where tip's parent is root? + continue; + } + glm::vec3 tipPosition = absolutePoses[tipIndex].trans; - glm::quat tipRotation = absolutePoses[tipIndex].rot; + //glm::quat tipRotation = absolutePoses[tipIndex].rot; // cache tip's parent's absolute rotation so we can recompute the tip's parent-relative // as we proceed walking down the joint chain - int pivotIndex = _skeleton->getParentIndex(tipIndex); - glm::quat tipParentRotation; - if (pivotIndex != -1) { - tipParentRotation = absolutePoses[pivotIndex].rot; - } + glm::quat tipParentRotation = absolutePoses[pivotIndex].rot; // descend toward root, pivoting each joint to get tip closer to target - int ancestorCount = 1; - while (pivotIndex != -1) { + while (pivotsParentIndex != -1) { // compute the two lines that should be aligned glm::vec3 jointPosition = absolutePoses[pivotIndex].trans; glm::vec3 leverArm = tipPosition - jointPosition; - glm::vec3 targetLine = target.getTranslation() - jointPosition; - // compute the swing that would get get tip closer - glm::vec3 axis = glm::cross(leverArm, targetLine); - float axisLength = glm::length(axis); glm::quat deltaRotation; - const float MIN_AXIS_LENGTH = 1.0e-4f; - if (axisLength > MIN_AXIS_LENGTH) { - // compute deltaRotation for alignment (swings tip closer to target) - axis /= axisLength; - float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); - - // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is - // still possible for the angle to be zero so we also check that to avoid unnecessary calculations. - const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; - if (angle > MIN_ADJUSTMENT_ANGLE) { - // reduce angle by a fraction (reduces IK swing contribution of this joint) - angle /= (float)ancestorCount; - deltaRotation = glm::angleAxis(angle, axis); - } - - // The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's - // new rotation and its target. We compute that delta here and rotate the tipJoint accordingly. - glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation(); - - // enforce tip's constraint - RotationConstraint* constraint = getConstraint(tipIndex); - if (constraint) { - bool constrained = constraint->apply(tipRelativeRotation); - if (constrained) { - // The tip's final parent-relative rotation violates its constraint - // so we try to twist this pivot to compensate. - glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; - glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation); - glm::quat swingPart; - glm::quat twistPart; - glm::vec3 axis = glm::normalize(deltaRotation * leverArm); - swingTwistDecomposition(missingRotation, axis, swingPart, twistPart); - deltaRotation = twistPart * deltaRotation; - } - // we update the tip rotation here to rotate it as close to its target orientation as possible - // before moving on to next pivot - tipRotation = tipParentRotation * tipRelativeRotation; - } - } - ++ancestorCount; - - int parentIndex = _skeleton->getParentIndex(pivotIndex); - if (parentIndex == -1) { - // TODO? apply constraints to root? - // TODO? harvest the root's transform as movement of entire skeleton? - } else { - // compute joint's new parent-relative rotation after swing - // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q - glm::quat newRot = glm::normalize(glm::inverse( - absolutePoses[parentIndex].rot) * - deltaRotation * - absolutePoses[pivotIndex].rot); - - // enforce pivot's constraint - RotationConstraint* constraint = getConstraint(pivotIndex); - if (constraint) { - bool constrained = constraint->apply(newRot); - if (constrained) { - // the constraint will modify the movement of the tip so we have to compute the modified - // model-frame deltaRotation - // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ - deltaRotation = absolutePoses[parentIndex].rot * - newRot * - glm::inverse(absolutePoses[pivotIndex].rot); + if (targetType == IKTarget::Type::RotationAndPosition) { + // compute the swing that would get get tip closer + glm::vec3 targetLine = target.getTranslation() - jointPosition; + glm::vec3 axis = glm::cross(leverArm, targetLine); + float axisLength = glm::length(axis); + const float MIN_AXIS_LENGTH = 1.0e-4f; + if (axisLength > MIN_AXIS_LENGTH) { + // compute deltaRotation for alignment (swings tip closer to target) + axis /= axisLength; + float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); + + // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is + // still possible for the angle to be zero so we also check that to avoid unnecessary calculations. + const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; + if (angle > MIN_ADJUSTMENT_ANGLE) { + // reduce angle by a fraction (for stability) + const float fraction = 0.5f; + angle *= fraction; + deltaRotation = glm::angleAxis(angle, axis); + + // The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's + // new rotation and its target. This is the final parent-relative rotation that the tip joint have + // make to achieve its target rotation. + glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * target.getRotation(); + + // enforce tip's constraint + RotationConstraint* constraint = getConstraint(tipIndex); + if (constraint) { + bool constrained = constraint->apply(tipRelativeRotation); + if (constrained) { + // The tip's final parent-relative rotation would violate its constraint + // so we try to pre-twist this pivot to compensate. + glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; + glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation); + glm::quat swingPart; + glm::quat twistPart; + glm::vec3 axis = glm::normalize(deltaRotation * leverArm); + swingTwistDecomposition(missingRotation, axis, swingPart, twistPart); + float dotSign = copysignf(1.0f, twistPart.w); + deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, fraction)) * deltaRotation; + } + } } } - - // store the rotation change in the accumulator - _accumulators[pivotIndex].add(newRot); + } else if (targetType == IKTarget::Type::HmdHead) { + /* TODO: implement this + // An HmdHead target slaves the orientation of the end-effector by distributing rotation + // deltas up the hierarchy. Its target position is enforced later by shifting the hips. + deltaRotation = target.getRotation() * glm::inverse(tipRotation); + */ } + + // compute joint's new parent-relative rotation after swing + // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q + glm::quat newRot = glm::normalize(glm::inverse( + absolutePoses[pivotsParentIndex].rot) * + deltaRotation * + absolutePoses[pivotIndex].rot); + + // enforce pivot's constraint + RotationConstraint* constraint = getConstraint(pivotIndex); + if (constraint) { + bool constrained = constraint->apply(newRot); + if (constrained) { + // the constraint will modify the movement of the tip so we have to compute the modified + // model-frame deltaRotation + // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ + deltaRotation = absolutePoses[pivotsParentIndex].rot * + newRot * + glm::inverse(absolutePoses[pivotIndex].rot); + } + } + + // store the rotation change in the accumulator + _accumulators[pivotIndex].add(newRot); + // this joint has been changed so we check to see if it has the lowest index if (pivotIndex < lowestMovedIndex) { lowestMovedIndex = pivotIndex; @@ -250,10 +258,10 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(pivotIndex); + pivotIndex = pivotsParentIndex; + pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); } } ++numLoops; @@ -276,17 +284,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector Date: Sat, 10 Oct 2015 19:23:21 -0700 Subject: [PATCH 12/57] experimental HMD hips tracking --- .../defaultAvatar_full/avatar-animation.json | 12 +++--- interface/src/avatar/MyAvatar.cpp | 4 +- .../animation/src/AnimInverseKinematics.cpp | 40 ++++++++++++++----- libraries/animation/src/IKTarget.cpp | 3 ++ libraries/animation/src/IKTarget.h | 1 + libraries/animation/src/Rig.cpp | 10 ++++- 6 files changed, 53 insertions(+), 17 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index e930f583f6..737a27be02 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -16,12 +16,14 @@ { "jointName": "RightHand", "positionVar": "rightHandPosition", - "rotationVar": "rightHandRotation" + "rotationVar": "rightHandRotation", + "typeVar": "rightHandType" }, { "jointName": "LeftHand", "positionVar": "leftHandPosition", - "rotationVar": "leftHandRotation" + "rotationVar": "leftHandRotation", + "typeVar": "leftHandType" }, { "jointName": "RightFoot", @@ -39,13 +41,13 @@ "jointName": "Neck", "positionVar": "neckPosition", "rotationVar": "neckRotation", - "typeVar": "headAndNeckType" + "typeVar": "neckType" }, { "jointName": "Head", "positionVar": "headPosition", "rotationVar": "headRotation", - "typeVar": "headAndNeckType" + "typeVar": "headType" } ] }, @@ -63,7 +65,7 @@ "id": "spineLean", "type": "manipulator", "data": { - "alpha": 1.0, + "alpha": 0.0, "joints": [ { "var": "lean", "jointName": "Spine" } ] diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..41e067c047 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -337,16 +337,18 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds // define a vertical capsule + static float adebug = 1.0f; // adebug const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { + if (!_straighteningLean && (capsuleCheck(diff, adebug * STRAIGHTENING_LEAN_CAPSULE_LENGTH, adebug * STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { // begin homing toward derived body position. _straighteningLean = true; _straighteningLeanAlpha = 0.0f; + adebug = 1000.0f; // adebug } else if (_straighteningLean) { diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 80c8152e3a..7e677abcba 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -98,8 +98,14 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); if (target.getType() != IKTarget::Type::Unknown) { AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - target.setPose(animVars.lookup(targetVar.rotationVar, defaultPose.rot), - animVars.lookup(targetVar.positionVar, defaultPose.trans)); + glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); + if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { + translation += _hipsOffset; + // HACK to test whether lifting idle hands will release the shoulders + translation.y += 0.10f; + } + target.setPose(rotation, translation); target.setIndex(targetVar.jointIndex); targets.push_back(target); if (targetVar.jointIndex > _maxTargetIndex) { @@ -125,6 +131,10 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } } + static int adebug = 0; ++adebug; bool verbose = (0 == (adebug % 100)); + if (verbose) { + std::cout << "adebug num targets = " << targets.size() << std::endl; // adebug + } } void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets) { @@ -175,7 +185,8 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetAbsolutePose(_headIndex, underPoses).trans; - glm::vec3 over = under; if (target.getType() == IKTarget::Type::RotationOnly) { - over = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; - } else if (target.getType() == IKTarget::Type::RotationAndPosition) { - over = target.getTranslation(); + // we want to shift the hips to bring the underpose closer + // to where the head happens to be (overpose) + glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans; + glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; + const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; + newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under); + } else if (target.getType() == IKTarget::Type::HmdHead) { + // we want to shift the hips to bring the head to its designated position + glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; + hmdHipsOffset = _hipsOffset + target.getTranslation() - actual; } - const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; - newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (over - under); } else if (target.getType() == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans; glm::vec3 targetPosition = target.getTranslation(); @@ -392,6 +408,10 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; _hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); + //_hipsOffset *= 0.0f; // adebug + if (glm::length2(hmdHipsOffset) < 100.0f) { + _hipsOffset = hmdHipsOffset; + } } } return _relativePoses; diff --git a/libraries/animation/src/IKTarget.cpp b/libraries/animation/src/IKTarget.cpp index a7e3296d35..b17f186b47 100644 --- a/libraries/animation/src/IKTarget.cpp +++ b/libraries/animation/src/IKTarget.cpp @@ -25,6 +25,9 @@ void IKTarget::setType(int type) { case (int)Type::HmdHead: _type = Type::HmdHead; break; + case (int)Type::HipsRelativeRotationAndPosition: + _type = Type::HipsRelativeRotationAndPosition; + break; default: _type = Type::Unknown; } diff --git a/libraries/animation/src/IKTarget.h b/libraries/animation/src/IKTarget.h index fa920d1511..983eafd9cb 100644 --- a/libraries/animation/src/IKTarget.h +++ b/libraries/animation/src/IKTarget.h @@ -18,6 +18,7 @@ public: RotationAndPosition, RotationOnly, HmdHead, + HipsRelativeRotationAndPosition, Unknown, }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3a1dafa10c..340c09060a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1058,9 +1058,11 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); - _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationAndPosition); + _animVars.set("headType", (int)IKTarget::Type::HmdHead); _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); + //_animVars.set("neckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target } else { @@ -1072,8 +1074,10 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("headPosition"); _animVars.set("headRotation", realLocalHeadOrientation); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); _animVars.unset("neckRotation"); + _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); } } else if (!_enableAnimGraph) { @@ -1131,16 +1135,20 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isLeftEnabled) { _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); + _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("leftHandPosition"); _animVars.unset("leftHandRotation"); + _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); + _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("rightHandPosition"); _animVars.unset("rightHandRotation"); + _animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } // set leftHand grab vars From 44243aaa523b424d257d282f03be6936819d88af Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 Oct 2015 11:34:07 -0700 Subject: [PATCH 13/57] remove some debug code --- libraries/animation/src/AnimInverseKinematics.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 7e677abcba..fd25705660 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -131,10 +131,6 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } } - static int adebug = 0; ++adebug; bool verbose = (0 == (adebug % 100)); - if (verbose) { - std::cout << "adebug num targets = " << targets.size() << std::endl; // adebug - } } void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets) { @@ -408,7 +404,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; _hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); - //_hipsOffset *= 0.0f; // adebug if (glm::length2(hmdHipsOffset) < 100.0f) { _hipsOffset = hmdHipsOffset; } From e1a5afcff317bf58190c8a52b34af624aebaa449 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 Oct 2015 11:42:03 -0700 Subject: [PATCH 14/57] re-enable avatar position update when moving HMD --- interface/src/avatar/MyAvatar.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 41e067c047..22816e9001 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -337,18 +337,16 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds // define a vertical capsule - static float adebug = 1.0f; // adebug const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straighteningLean && (capsuleCheck(diff, adebug * STRAIGHTENING_LEAN_CAPSULE_LENGTH, adebug * STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { + if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { // begin homing toward derived body position. _straighteningLean = true; _straighteningLeanAlpha = 0.0f; - adebug = 1000.0f; // adebug } else if (_straighteningLean) { From 7b41efcf8622b4ac2a269b7e592e757615ec5c76 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 13:35:21 -0700 Subject: [PATCH 15/57] All file types eligible for upload to asset server --- interface/src/Application.cpp | 128 ++++++++++---------- interface/src/Application.h | 2 +- interface/src/ui/AssetUploadDialogFactory.h | 1 - 3 files changed, 64 insertions(+), 67 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cfeb77f638..0d51ccbbc7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -126,6 +126,7 @@ #include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" +#include "ui/AssetUploadDialogFactory.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" @@ -159,6 +160,7 @@ static const QString SVO_JSON_EXTENSION = ".svo.json"; static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; +static const QString OBJ_EXTENSION = ".obj"; static const int MIRROR_VIEW_TOP_PADDING = 5; static const int MIRROR_VIEW_LEFT_PADDING = 10; @@ -179,9 +181,6 @@ static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; -const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml"; -const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; - #ifndef __APPLE__ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); #else @@ -197,8 +196,7 @@ const QHash Application::_acceptedExtensi { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl }, - { FBX_EXTENSION, &Application::askToUploadAsset } + { FST_EXTENSION, &Application::askToSetAvatarUrl } }; #ifdef Q_OS_WIN @@ -2023,21 +2021,14 @@ void Application::dropEvent(QDropEvent *event) { const QMimeData* mimeData = event->mimeData(); for (auto& url : mimeData->urls()) { QString urlString = url.toString(); - if (canAcceptURL(urlString) && acceptURL(urlString)) { + if (acceptURL(urlString, true)) { event->acceptProposedAction(); } } } void Application::dragEnterEvent(QDragEnterEvent* event) { - const QMimeData* mimeData = event->mimeData(); - for (auto& url : mimeData->urls()) { - auto urlString = url.toString(); - if (canAcceptURL(urlString)) { - event->acceptProposedAction(); - break; - } - } + event->acceptProposedAction(); } bool Application::acceptSnapshot(const QString& urlString) { @@ -3973,26 +3964,26 @@ bool Application::canAcceptURL(const QString& urlString) { return false; } -bool Application::acceptURL(const QString& urlString) { +bool Application::acceptURL(const QString& urlString, bool defaultUpload) { if (urlString.startsWith(HIFI_URL_SCHEME)) { // this is a hifi URL - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Qt::AutoConnection, Q_ARG(const QString&, urlString)); return true; - } else { - QUrl url(urlString); - QHashIterator i(_acceptedExtensions); - QString lowerPath = url.path().toLower(); - while (i.hasNext()) { - i.next(); - if (lowerPath.endsWith(i.key())) { - AcceptURLMethod method = i.value(); - (this->*method)(urlString); - return true; - } + } + + QUrl url(urlString); + QHashIterator i(_acceptedExtensions); + QString lowerPath = url.path().toLower(); + while (i.hasNext()) { + i.next(); + if (lowerPath.endsWith(i.key())) { + AcceptURLMethod method = i.value(); + return (this->*method)(urlString); } } - return false; + + return defaultUpload && askToUploadAsset(urlString); } void Application::setSessionUUID(const QUuid& sessionUUID) { @@ -4076,8 +4067,36 @@ bool Application::askToUploadAsset(const QString& filename) { QUrl url { filename }; if (auto upload = DependencyManager::get()->createUpload(url.toLocalFile())) { + + QMessageBox messageBox; + messageBox.setWindowTitle("Asset upload"); + messageBox.setText("You are about to upload the following file to the asset server:\n" + + url.toDisplayString()); + messageBox.setInformativeText("Do you want to continue?"); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Ok); + + // Option to drop model in world for models + if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) { + auto checkBox = new QCheckBox(&messageBox); + checkBox->setText("Drop model in world."); + messageBox.setCheckBox(checkBox); + } + + if (messageBox.exec() != QMessageBox::Ok) { + upload->deleteLater(); + return false; + } + // connect to the finished signal so we know when the AssetUpload is done - QObject::connect(upload, &AssetUpload::finished, this, &Application::assetUploadFinished); + if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) { + // Custom behavior for models + QObject::connect(upload, &AssetUpload::finished, this, &Application::assetUploadFinished); + } else { + QObject::connect(upload, &AssetUpload::finished, + &AssetUploadDialogFactory::getInstance(), + &AssetUploadDialogFactory::handleUploadFinished); + } // start the upload now upload->start(); @@ -4090,46 +4109,25 @@ bool Application::askToUploadAsset(const QString& filename) { } void Application::assetUploadFinished(AssetUpload* upload, const QString& hash) { - if (upload->getError() != AssetUpload::NoError) { - // figure out the right error message for the message box - QString additionalError; + auto filename = QFileInfo(upload->getFilename()).fileName(); + + if ((upload->getError() == AssetUpload::NoError) && + (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION))) { - switch (upload->getError()) { - case AssetUpload::PermissionDenied: - additionalError = "You do not have permission to upload content to this asset-server."; - break; - case AssetUpload::TooLarge: - additionalError = "The uploaded content was too large and could not be stored in the asset-server."; - break; - case AssetUpload::FileOpenError: - additionalError = "The file could not be opened. Please check your permissions and try again."; - break; - case AssetUpload::NetworkError: - additionalError = "The file could not be opened. Please check your network connectivity."; - break; - default: - // not handled, do not show a message box - return; - } + auto entities = DependencyManager::get(); - // display a message box with the error - auto filename = QFileInfo(upload->getFilename()).fileName(); - QString errorMessage = QString("Failed to upload %1.\n\n%2").arg(filename, additionalError); - QMessageBox::warning(_window, "Failed Upload", errorMessage); + EntityItemProperties properties; + properties.setType(EntityTypes::Model); + properties.setModelURL(QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension())); + properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f); + properties.setName(QUrl(upload->getFilename()).fileName()); + + entities->addEntity(properties); + + upload->deleteLater(); + } else { + AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash); } - - auto entities = DependencyManager::get(); - auto myAvatar = getMyAvatar(); - - EntityItemProperties properties; - properties.setType(EntityTypes::Model); - properties.setModelURL(QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension())); - properties.setPosition(myAvatar->getPosition() + myAvatar->getOrientation() * Vectors::FRONT * 2.0f); - properties.setName(QUrl(upload->getFilename()).fileName()); - - entities->addEntity(properties); - - upload->deleteLater(); } ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, diff --git a/interface/src/Application.h b/interface/src/Application.h index 8444eab2b6..3e443ea843 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -211,7 +211,7 @@ public: void setScriptsLocation(const QString& scriptsLocation); bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url); + bool acceptURL(const QString& url, bool defaultUpload = false); void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index 36c5d10595..a8e8765f20 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -30,7 +30,6 @@ public: public slots: void showDialog(); -private slots: void handleUploadFinished(AssetUpload* upload, const QString& hash); private: From 22786dea7d3d3438888fe46cbadc15215b5b5fce Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 Oct 2015 13:35:26 -0700 Subject: [PATCH 16/57] cleanup --- .../defaultAvatar_full/avatar-animation.json | 12 +++++------ .../animation/src/AnimInverseKinematics.cpp | 20 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 737a27be02..682e0be1bf 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -27,15 +27,15 @@ }, { "jointName": "RightFoot", - "positionVar": "rightToePosition", - "rotationVar": "rightToeRotation", - "typeVar": "rightToeType" + "positionVar": "rightFootPosition", + "rotationVar": "rightFootRotation", + "typeVar": "rightFootType" }, { "jointName": "LeftFoot", - "positionVar": "leftToePosition", - "rotationVar": "leftToeRotation", - "typeVar": "leftToeType" + "positionVar": "leftFootPosition", + "rotationVar": "leftFootRotation", + "typeVar": "leftFootType" }, { "jointName": "Neck", diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index fd25705660..a1c6848b0c 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -102,8 +102,6 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { translation += _hipsOffset; - // HACK to test whether lifting idle hands will release the shoulders - translation.y += 0.10f; } target.setPose(rotation, translation); target.setIndex(targetVar.jointIndex); @@ -363,20 +361,18 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } else { // shift the hips according to the offset from the previous frame float offsetLength = glm::length(_hipsOffset); - const float MIN_OFFSET_LENGTH = 0.03f; - if (offsetLength > MIN_OFFSET_LENGTH) { - // but only if actual offset is long enough - float scaleFactor = ((offsetLength - MIN_OFFSET_LENGTH) / offsetLength); + const float MIN_HIPS_OFFSET_LENGTH = 0.03f; + if (offsetLength > MIN_HIPS_OFFSET_LENGTH) { + // but only if offset is long enough + float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength); _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _hipsOffset; } solveWithCyclicCoordinateDescent(targets); - // compute the new target hips offset (for next frame) // by looking for discrepancies between where a targeted endEffector is // and where it wants to be (after IK solutions are done) - glm::vec3 hmdHipsOffset(100.0f); glm::vec3 newHipsOffset = Vectors::ZERO; for (auto& target: targets) { int targetIndex = target.getIndex(); @@ -392,7 +388,10 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } else if (target.getType() == IKTarget::Type::HmdHead) { // we want to shift the hips to bring the head to its designated position glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; - hmdHipsOffset = _hipsOffset + target.getTranslation() - actual; + _hipsOffset += target.getTranslation() - actual; + // and ignore all other targets + newHipsOffset = _hipsOffset; + break; } } else if (target.getType() == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans; @@ -404,9 +403,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // smooth transitions by relaxing _hipsOffset toward the new value const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; _hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE); - if (glm::length2(hmdHipsOffset) < 100.0f) { - _hipsOffset = hmdHipsOffset; - } } } return _relativePoses; From c666c9fb9b5df854990826a27d2b665aeb86a51e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 13:40:33 -0700 Subject: [PATCH 17/57] Fix warning --- .../input-plugins/src/input-plugins/StandardController.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 4fb4a23654..9b89c081b3 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -17,8 +17,6 @@ const float CONTROLLER_THRESHOLD = 0.3f; -const float MAX_AXIS = 32768.0f; - StandardController::~StandardController() { } From 8ebdd25b7123e9620545471ac9783ca9a66e4c79 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 12 Oct 2015 14:07:23 -0700 Subject: [PATCH 18/57] more cleanup --- .../animation/src/AnimInverseKinematics.cpp | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index a1c6848b0c..17a1af1969 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -56,9 +56,9 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con } void AnimInverseKinematics::setTargetVars( - const QString& jointName, - const QString& positionVar, - const QString& rotationVar, + const QString& jointName, + const QString& positionVar, + const QString& rotationVar, const QString& typeVar) { // if there are dups, last one wins. bool found = false; @@ -98,7 +98,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); if (target.getType() != IKTarget::Type::Unknown) { AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { translation += _hipsOffset; @@ -168,7 +168,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorapply(tipRelativeRotation); if (constrained) { - // The tip's final parent-relative rotation would violate its constraint + // The tip's final parent-relative rotation would violate its constraint // so we try to pre-twist this pivot to compensate. glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation; glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation); @@ -225,11 +225,12 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetAbsolutePose(_headIndex, underPoses).trans; glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; From eea89ba010bedfe3052bf14e68450a6ba404e129 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 14:26:28 -0700 Subject: [PATCH 19/57] CR - Rename method --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0d51ccbbc7..9e9830d977 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4091,7 +4091,7 @@ bool Application::askToUploadAsset(const QString& filename) { // connect to the finished signal so we know when the AssetUpload is done if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) { // Custom behavior for models - QObject::connect(upload, &AssetUpload::finished, this, &Application::assetUploadFinished); + QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished); } else { QObject::connect(upload, &AssetUpload::finished, &AssetUploadDialogFactory::getInstance(), @@ -4108,7 +4108,7 @@ bool Application::askToUploadAsset(const QString& filename) { return false; } -void Application::assetUploadFinished(AssetUpload* upload, const QString& hash) { +void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) { auto filename = QFileInfo(upload->getFilename()).fileName(); if ((upload->getError() == AssetUpload::NoError) && diff --git a/interface/src/Application.h b/interface/src/Application.h index 3e443ea843..a2125e7e09 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -329,7 +329,7 @@ private slots: bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); bool askToUploadAsset(const QString& asset); - void assetUploadFinished(AssetUpload* upload, const QString& hash); + void modelUploadFinished(AssetUpload* upload, const QString& hash); void setSessionUUID(const QUuid& sessionUUID); void domainChanged(const QString& domainHostname); From 84f0c404adf6d3b5f628cf2645fd90eb40dca687 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 14:55:52 -0700 Subject: [PATCH 20/57] fix bug related to camera lock for entity orbit and pan --- examples/edit.js | 593 +++++++++++++++++++------ examples/libraries/entityCameraTool.js | 208 +++++++-- 2 files changed, 622 insertions(+), 179 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 0afc3ec1b6..09d30c712b 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -39,7 +39,9 @@ var lightOverlayManager = new LightOverlayManager(); var cameraManager = new CameraManager(); var grid = Grid(); -gridTool = GridTool({ horizontalGrid: grid }); +gridTool = GridTool({ + horizontalGrid: grid +}); gridTool.setVisible(false); var entityListTool = EntityListTool(); @@ -92,8 +94,8 @@ var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissi var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." var modelURLs = [ - "Insert the URL to your FBX" - ]; + "Insert the URL to your FBX" +]; var mode = 0; var isActive = false; @@ -109,20 +111,30 @@ var importingSVOImageOverlay = Overlays.addOverlay("image", { width: 20, height: 20, alpha: 1.0, - color: { red: 255, green: 255, blue: 255 }, + color: { + red: 255, + green: 255, + blue: 255 + }, x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH, y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT, visible: false, }); var importingSVOTextOverlay = Overlays.addOverlay("text", { - font: { size: 14 }, + font: { + size: 14 + }, text: "Importing SVO...", leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN, x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, width: IMPORTING_SVO_OVERLAY_WIDTH, height: IMPORTING_SVO_OVERLAY_HEIGHT, - backgroundColor: { red: 80, green: 80, blue: 80 }, + backgroundColor: { + red: 80, + green: 80, + blue: 80 + }, backgroundAlpha: 0.7, visible: false, }); @@ -131,7 +143,7 @@ var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); marketplaceWindow.setVisible(false); -var toolBar = (function () { +var toolBar = (function() { var that = {}, toolBar, activeButton, @@ -146,7 +158,7 @@ var toolBar = (function () { browseMarketplaceButton; function initialize() { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function (windowDimensions, toolbar) { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) { return { x: windowDimensions.x - 8 - toolbar.width, y: (windowDimensions.y - toolbar.height) / 2 @@ -163,7 +175,12 @@ var toolBar = (function () { activeButton = toolBar.addTool({ imageURL: toolIconUrl + "edit-status.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -172,7 +189,12 @@ var toolBar = (function () { newModelButton = toolBar.addTool({ imageURL: toolIconUrl + "upload.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -181,7 +203,12 @@ var toolBar = (function () { newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -190,7 +217,12 @@ var toolBar = (function () { newSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "add-sphere.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -199,7 +231,12 @@ var toolBar = (function () { newLightButton = toolBar.addTool({ imageURL: toolIconUrl + "light.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -208,7 +245,12 @@ var toolBar = (function () { newTextButton = toolBar.addTool({ imageURL: toolIconUrl + "add-text.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -217,7 +259,12 @@ var toolBar = (function () { newWebButton = toolBar.addTool({ imageURL: "https://hifi-public.s3.amazonaws.com/images/www.svg", - subImage: { x: 0, y: 0, width: 128, height: 128 }, + subImage: { + x: 0, + y: 0, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -226,7 +273,12 @@ var toolBar = (function () { newZoneButton = toolBar.addTool({ imageURL: toolIconUrl + "zonecube_text.svg", - subImage: { x: 0, y: 128, width: 128, height: 128 }, + subImage: { + x: 0, + y: 128, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -235,7 +287,12 @@ var toolBar = (function () { newPolyVoxButton = toolBar.addTool({ imageURL: toolIconUrl + "polyvox.svg", - subImage: { x: 0, y: 0, width: 256, height: 256 }, + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -293,6 +350,7 @@ var toolBar = (function () { var RESIZE_INTERVAL = 50; var RESIZE_TIMEOUT = 120000; // 2 minutes var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; + function addModel(url) { var entityID = createNewEntity({ type: "Model", @@ -314,7 +372,7 @@ var toolBar = (function () { var entityID = null; if (position != null) { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions), - properties.position = position; + properties.position = position; entityID = Entities.addEntity(properties); if (dragOnCreate) { @@ -329,12 +387,15 @@ var toolBar = (function () { var newModelButtonDown = false; var browseMarketplaceButtonDown = false; - that.mousePressEvent = function (event) { + that.mousePressEvent = function(event) { var clickedOverlay, url, file; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (activeButton === toolBar.clicked(clickedOverlay)) { that.setActive(!isActive); @@ -360,7 +421,11 @@ var toolBar = (function () { createNewEntity({ type: "Box", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -370,7 +435,11 @@ var toolBar = (function () { createNewEntity({ type: "Sphere", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -381,7 +450,11 @@ var toolBar = (function () { type: "Light", dimensions: DEFAULT_LIGHT_DIMENSIONS, isSpotlight: false, - color: { red: 150, green: 150, blue: 150 }, + color: { + red: 150, + green: 150, + blue: 150 + }, constantAttenuation: 1, linearAttenuation: 0, @@ -396,9 +469,21 @@ var toolBar = (function () { if (newTextButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Text", - dimensions: { x: 0.65, y: 0.3, z: 0.01 }, - backgroundColor: { red: 64, green: 64, blue: 64 }, - textColor: { red: 255, green: 255, blue: 255 }, + dimensions: { + x: 0.65, + y: 0.3, + z: 0.01 + }, + backgroundColor: { + red: 64, + green: 64, + blue: 64 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, text: "some text", lineHeight: 0.06 }); @@ -409,7 +494,11 @@ var toolBar = (function () { if (newWebButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Web", - dimensions: { x: 1.6, y: 0.9, z: 0.01 }, + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, sourceUrl: "https://highfidelity.com/", }); @@ -419,7 +508,11 @@ var toolBar = (function () { if (newZoneButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Zone", - dimensions: { x: 10, y: 10, z: 10 }, + dimensions: { + x: 10, + y: 10, + z: 10 + }, }); return true; @@ -428,27 +521,83 @@ var toolBar = (function () { if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { var polyVoxId = createNewEntity({ type: "PolyVox", - dimensions: { x: 10, y: 10, z: 10 }, - voxelVolumeSize: {x:16, y:16, z:16}, + dimensions: { + x: 10, + y: 10, + z: 10 + }, + voxelVolumeSize: { + x: 16, + y: 16, + z: 16 + }, voxelSurfaceStyle: 2 }); for (var x = 1; x <= 14; x++) { - Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 14 + }, 255); } for (var y = 2; y <= 13; y++) { - Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 14 + }, 255); } for (var z = 2; z <= 13; z++) { - Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: 14, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 14, + z: z + }, 255); } @@ -461,7 +610,10 @@ var toolBar = (function () { that.mouseReleaseEvent = function(event) { var handled = false; if (newModelButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (newModelButton === toolBar.clicked(clickedOverlay)) { url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url !== null && url !== "") { @@ -470,7 +622,10 @@ var toolBar = (function () { handled = true; } } else if (browseMarketplaceButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)"); if (url !== null && url !== "") { @@ -497,7 +652,7 @@ var toolBar = (function () { } }); - that.cleanup = function () { + that.cleanup = function() { toolBar.cleanup(); }; @@ -568,17 +723,26 @@ function findClickedEntity(event) { } var foundEntity = result.entityID; - return { pickRay: pickRay, entityID: foundEntity }; + return { + pickRay: pickRay, + entityID: foundEntity + }; } var mouseHasMovedSincePress = false; var mousePressStartTime = 0; -var mousePressStartPosition = { x: 0, y: 0 }; +var mousePressStartPosition = { + x: 0, + y: 0 +}; var mouseDown = false; function mousePressEvent(event) { mouseDown = true; - mousePressStartPosition = { x: event.x, y: event.y }; + mousePressStartPosition = { + x: event.x, + y: event.y + }; mousePressStartTime = Date.now(); mouseHasMovedSincePress = false; mouseCapturedByTool = false; @@ -605,9 +769,11 @@ var IDLE_MOUSE_TIMEOUT = 200; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; var lastMouseMoveEvent = null; + function mouseMoveEventBuffered(event) { lastMouseMoveEvent = event; } + function mouseMove(event) { if (mouseDown && !mouseHasMovedSincePress) { var timeSincePressMicro = Date.now() - mousePressStartTime; @@ -647,7 +813,10 @@ function mouseMove(event) { return; } - lastMousePosition = { x: event.x, y: event.y }; + lastMousePosition = { + x: event.x, + y: event.y + }; idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); } @@ -660,22 +829,25 @@ function highlightEntityUnderCursor(position, accurateRay) { var pickRay = Camera.computePickRay(position.x, position.y); var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay); if (entityIntersection.accurate) { - if(highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { + if (highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { id: -1 }; + highlightedEntityID = { + id: -1 + }; } var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), - entityIntersection.properties.position)) * 180 / 3.14; + entityIntersection.properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (entityIntersection.entityID && sizeOK) { if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + Entities.editEntity(entityIntersection.entityID, { + glowLevel: 0.25 + }); } highlightedEntityID = entityIntersection.entityID; selectionDisplay.highlightSelectable(entityIntersection.entityID); @@ -693,18 +865,23 @@ function mouseReleaseEvent(event) { lastMouseMoveEvent = null; } if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) { + return true; } if (placingEntityID) { + if (isActive) { + selectionManager.setSelections([placingEntityID]); } placingEntityID = null; } if (isActive && selectionManager.hasSelection()) { + tooltip.show(false); } if (mouseCapturedByTool) { + return; } @@ -757,8 +934,7 @@ function mouseClickEvent(event) { var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (0 < x && sizeOK) { entitySelected = true; @@ -779,8 +955,8 @@ function mouseClickEvent(event) { if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } @@ -813,44 +989,125 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); // added it. var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); + function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Physics" + }); if (!Menu.menuItemExists("Edit", "Delete")) { print("no delete... adding ours"); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", - shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Delete", + shortcutKeyEvent: { + text: "backspace" + }, + afterItem: "Models" + }); modelMenuAddedDelete = true; } else { print("delete exists... don't add ours"); } - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Entity List...", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A", - afterItem: "Allow Selecting of Lights" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T", - afterItem: "Select All Entities In Box" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Entity List...", + shortcutKey: "CTRL+META+L", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Large Models", + shortcutKey: "CTRL+META+L", + afterItem: "Entity List...", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Small Models", + shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Lights", + shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", + isCheckable: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities In Box", + shortcutKey: "CTRL+SHIFT+META+A", + afterItem: "Allow Selecting of Lights" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities Touching Box", + shortcutKey: "CTRL+SHIFT+META+T", + afterItem: "Select All Entities In Box" + }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Settings" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Export Entities", + shortcutKey: "CTRL+META+E", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities", + shortcutKey: "CTRL+META+I", + afterItem: "Export Entities" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities from URL", + shortcutKey: "CTRL+META+U", + afterItem: "Import Entities" + }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, - isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, - isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_EASE_ON_FOCUS, + afterItem: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + afterItem: MENU_EASE_ON_FOCUS, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, + afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" + }); Entities.setLightsArePickable(false); } @@ -903,7 +1160,7 @@ var lastOrientation = null; var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays -Script.update.connect(function (deltaTime) { +Script.update.connect(function(deltaTime) { progressDialog.move(); selectionDisplay.checkMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); @@ -920,16 +1177,14 @@ Script.update.connect(function (deltaTime) { }); function insideBox(center, dimensions, point) { - return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) - && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) - && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); + return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); } function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { if (selectionManager.hasSelection()) { // Get all entities touching the bounding box of the current selection var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, - Vec3.multiply(selectionManager.worldDimensions, 0.5)); + Vec3.multiply(selectionManager.worldDimensions, 0.5)); var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); if (!keepIfTouching) { @@ -941,9 +1196,13 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } else { isValid = function(position) { var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation), - Vec3.subtract(position, - selectionManager.localPosition)); - return insideBox({ x: 0, y: 0, z: 0 }, selectionManager.localDimensions, localPosition); + Vec3.subtract(position, + selectionManager.localPosition)); + return insideBox({ + x: 0, + y: 0, + z: 0 + }, selectionManager.localDimensions, localPosition); } } for (var i = 0; i < entities.length; ++i) { @@ -1038,15 +1297,11 @@ function getPositionToCreateEntity() { var placementPosition = Vec3.sum(Camera.position, offset); var cameraPosition = Camera.position; - + var HALF_TREE_SCALE = 16384; - var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE - || Math.abs(cameraPosition.y) > HALF_TREE_SCALE - || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; - var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE - || Math.abs(placementPosition.y) > HALF_TREE_SCALE - || Math.abs(placementPosition.z) > HALF_TREE_SCALE; + var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE || Math.abs(cameraPosition.y) > HALF_TREE_SCALE || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; + var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE || Math.abs(placementPosition.y) > HALF_TREE_SCALE || Math.abs(placementPosition.z) > HALF_TREE_SCALE; if (cameraOutOfBounds && placementOutOfBounds) { return null; @@ -1065,14 +1320,22 @@ function importSVO(importURL) { return; } - Overlays.editOverlay(importingSVOTextOverlay, { visible: true }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: true }); + Overlays.editOverlay(importingSVOTextOverlay, { + visible: true + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: true + }); var success = Clipboard.importEntities(importURL); if (success) { var VERY_LARGE = 10000; - var position = { x: 0, y: 0, z: 0 }; + var position = { + x: 0, + y: 0, + z: 0 + }; if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { position = getPositionToCreateEntity(); } @@ -1091,8 +1354,12 @@ function importSVO(importURL) { Window.alert("There was an error importing the entity file."); } - Overlays.editOverlay(importingSVOTextOverlay, { visible: false }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: false }); + Overlays.editOverlay(importingSVOTextOverlay, { + visible: false + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: false + }); } Window.svoImportRequested.connect(importSVO); @@ -1104,7 +1371,7 @@ Controller.keyPressEvent.connect(function(event) { } }); -Controller.keyReleaseEvent.connect(function (event) { +Controller.keyReleaseEvent.connect(function(event) { if (isActive) { cameraManager.keyReleaseEvent(event); } @@ -1120,8 +1387,8 @@ Controller.keyReleaseEvent.connect(function (event) { if (selectionManager.hasSelection()) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } else if (event.text == '[') { @@ -1131,7 +1398,11 @@ Controller.keyReleaseEvent.connect(function (event) { } else if (event.text == 'g') { if (isActive && selectionManager.hasSelection()) { var newPosition = selectionManager.worldPosition; - newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); + newPosition = Vec3.subtract(newPosition, { + x: 0, + y: selectionManager.worldDimensions.y * 0.5, + z: 0 + }); grid.setPosition(newPosition); } } @@ -1140,8 +1411,7 @@ Controller.keyReleaseEvent.connect(function (event) { // When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently // possible to create an entity with a specific id, earlier undo commands to the deleted entity // will fail if there isn't a way to find the new entity id. -DELETED_ENTITY_MAP = { -} +DELETED_ENTITY_MAP = {} function applyEntityProperties(data) { var properties = data.setProperties; @@ -1293,7 +1563,11 @@ PropertiesTool = function(opts) { if (selectionManager.hasSelection()) { selectionManager.saveProperties(); var dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2); - var diff = { x: 0, y: dY, z: 0 }; + var diff = { + x: 0, + y: dY, + z: 0 + }; for (var i = 0; i < selectionManager.selections.length; i++) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; var newPosition = Vec3.sum(properties.position, diff); @@ -1311,7 +1585,11 @@ PropertiesTool = function(opts) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2; var dY = grid.getOrigin().y - bottomY; - var diff = { x: 0, y: dY, z: 0 }; + var diff = { + x: 0, + y: dY, + z: 0 + }; var newPosition = Vec3.sum(properties.position, diff); Entities.editEntity(selectionManager.selections[i], { position: newPosition, @@ -1328,10 +1606,8 @@ PropertiesTool = function(opts) { var naturalDimensions = properties.naturalDimensions; // If any of the natural dimensions are not 0, resize - if (properties.type == "Model" && naturalDimensions.x == 0 - && naturalDimensions.y == 0 && naturalDimensions.z == 0) { - Window.alert("Cannot reset entity to its natural dimensions: Model URL" - + " is invalid or the model has not yet been loaded."); + if (properties.type == "Model" && naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + Window.alert("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); } else { Entities.editEntity(selectionManager.selections[i], { dimensions: properties.naturalDimensions, @@ -1370,12 +1646,16 @@ PropertiesTool = function(opts) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; if (properties.type == "Zone") { var centerOfZone = properties.boundingBox.center; - var atmosphereCenter = { x: centerOfZone.x, - y: centerOfZone.y - properties.atmosphere.innerRadius, - z: centerOfZone.z }; + var atmosphereCenter = { + x: centerOfZone.x, + y: centerOfZone.y - properties.atmosphere.innerRadius, + z: centerOfZone.z + }; Entities.editEntity(selectionManager.selections[i], { - atmosphere: { center: atmosphereCenter }, + atmosphere: { + center: atmosphereCenter + }, }); } } @@ -1399,11 +1679,23 @@ PopupMenu = function() { var overlays = []; var overlayInfo = {}; - var upColor = { red: 0, green: 0, blue: 0 }; - var downColor = { red: 192, green: 192, blue: 192 }; - var overColor = { red: 128, green: 128, blue: 128 }; + var upColor = { + red: 0, + green: 0, + blue: 0 + }; + var downColor = { + red: 192, + green: 192, + blue: 192 + }; + var overColor = { + red: 128, + green: 128, + blue: 128 + }; - self.onSelectMenuItem = function() { }; + self.onSelectMenuItem = function() {}; self.addMenuItem = function(name) { var id = Overlays.addOverlay("text", { @@ -1414,16 +1706,22 @@ PopupMenu = function() { leftMargin: TEXT_MARGIN, width: 210, height: MENU_ITEM_HEIGHT, - font: { size: 12 }, + font: { + size: 12 + }, visible: false, }); overlays.push(id); - overlayInfo[id] = { name: name }; + overlayInfo[id] = { + name: name + }; return id; }; self.updateMenuItemText = function(id, newText) { - Overlays.editOverlay(id, { text: newText }); + Overlays.editOverlay(id, { + text: newText + }); }; self.setPosition = function(x, y) { @@ -1436,17 +1734,22 @@ PopupMenu = function() { } }; - self.onSelected = function() { }; + self.onSelected = function() {}; var pressingOverlay = null; var hoveringOverlay = null; self.mousePressEvent = function(event) { if (event.isLeftButton) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (overlay in overlayInfo) { pressingOverlay = overlay; - Overlays.editOverlay(pressingOverlay, { backgroundColor: downColor }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: downColor + }); } else { self.hide(); } @@ -1455,14 +1758,21 @@ PopupMenu = function() { }; self.mouseMoveEvent = function(event) { if (visible) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (!pressingOverlay) { if (hoveringOverlay != null && overlay != hoveringOverlay) { - Overlays.editOverlay(hoveringOverlay, { backgroundColor: upColor}); + Overlays.editOverlay(hoveringOverlay, { + backgroundColor: upColor + }); hoveringOverlay = null; } if (overlay != hoveringOverlay && overlay in overlayInfo) { - Overlays.editOverlay(overlay, { backgroundColor: overColor }); + Overlays.editOverlay(overlay, { + backgroundColor: overColor + }); hoveringOverlay = overlay; } } @@ -1470,12 +1780,17 @@ PopupMenu = function() { return false; }; self.mouseReleaseEvent = function(event) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (pressingOverlay != null) { if (overlay == pressingOverlay) { self.onSelectMenuItem(overlayInfo[overlay].name); } - Overlays.editOverlay(pressingOverlay, { backgroundColor: upColor }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: upColor + }); pressingOverlay = null; self.hide(); } @@ -1487,7 +1802,9 @@ PopupMenu = function() { if (newVisible != visible) { visible = newVisible; for (var key in overlayInfo) { - Overlays.editOverlay(key, { visible: newVisible }); + Overlays.editOverlay(key, { + visible: newVisible + }); } } } @@ -1527,4 +1844,4 @@ propertyMenu.onSelectMenuItem = function(name) { var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); -propertiesTool = PropertiesTool(); +propertiesTool = PropertiesTool(); \ No newline at end of file diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index a3b817e300..d304a6382e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -118,8 +118,16 @@ CameraManager = function() { that.targetYaw = 0; that.targetPitch = 0; - that.focalPoint = { x: 0, y: 0, z: 0 }; - that.targetFocalPoint = { x: 0, y: 0, z: 0 }; + that.focalPoint = { + x: 0, + y: 0, + z: 0 + }; + that.targetFocalPoint = { + x: 0, + y: 0, + z: 0 + }; easing = false; easingTime = 0; @@ -127,13 +135,18 @@ CameraManager = function() { that.previousCameraMode = null; - that.lastMousePosition = { x: 0, y: 0 }; + that.lastMousePosition = { + x: 0, + y: 0 + }; that.enable = function() { if (Camera.mode == "independent" || that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.captureKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = true; @@ -143,7 +156,7 @@ CameraManager = function() { that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = that.zoomDistance + 3.0; var focalPoint = Vec3.sum(Camera.getPosition(), - Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); @@ -169,7 +182,9 @@ CameraManager = function() { if (!that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.releaseKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = false; @@ -335,19 +350,27 @@ CameraManager = function() { var hasDragged = false; that.mousePressEvent = function(event) { + if (cameraTool.mousePressEvent(event)) { + return true; } + if (!that.enabled) return; if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + that.mode = MODE_ORBIT; } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { + + that.mode = MODE_PAN; } - if (that.mode != MODE_INACTIVE) { + if (that.mode !== MODE_INACTIVE) { + + hasDragged = false; return true; @@ -357,10 +380,12 @@ CameraManager = function() { } that.mouseReleaseEvent = function(event) { + if (!that.enabled) return; - Window.setCursorVisible(true); that.mode = MODE_INACTIVE; + Window.setCursorVisible(true); + } that.keyPressEvent = function(event) { @@ -396,15 +421,31 @@ CameraManager = function() { return; } - var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); - var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); + var yRot = Quat.angleAxis(that.yaw, { + x: 0, + y: 1, + z: 0 + }); + var xRot = Quat.angleAxis(that.pitch, { + x: 1, + y: 0, + z: 0 + }); var q = Quat.multiply(yRot, xRot); var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); Camera.setPosition(Vec3.sum(that.focalPoint, pos)); - yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); - xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); + yRot = Quat.angleAxis(that.yaw - 180, { + x: 0, + y: 1, + z: 0 + }); + xRot = Quat.angleAxis(-that.pitch, { + x: 1, + y: 0, + z: 0 + }); q = Quat.multiply(yRot, xRot); if (easing) { @@ -483,7 +524,7 @@ CameraManager = function() { } }); - Controller.keyReleaseEvent.connect(function (event) { + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "ESC" && that.enabled) { Camera.mode = lastAvatarCameraMode; cameraManager.disable(true); @@ -503,9 +544,21 @@ CameraManager = function() { CameraTool = function(cameraManager) { var that = {}; - var RED = { red: 191, green: 78, blue: 38 }; - var GREEN = { red: 26, green: 193, blue: 105 }; - var BLUE = { red: 0, green: 131, blue: 204 }; + var RED = { + red: 191, + green: 78, + blue: 38 + }; + var GREEN = { + red: 26, + green: 193, + blue: 105 + }; + var BLUE = { + red: 0, + green: 131, + blue: 204 + }; var BORDER_WIDTH = 1; @@ -513,10 +566,10 @@ CameraTool = function(cameraManager) { var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, - var ORIENTATION_OVERLAY_OFFSET = { - x: 30, - y: 30, - } + var ORIENTATION_OVERLAY_OFFSET = { + x: 30, + y: 30, + } var UI_WIDTH = 70; var UI_HEIGHT = 70; @@ -536,7 +589,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT + BORDER_WIDTH * 2, alpha: 0, text: "", - backgroundColor: { red: 101, green: 101, blue: 101 }, + backgroundColor: { + red: 101, + green: 101, + blue: 101 + }, backgroundAlpha: 1.0, visible: false, }); @@ -548,7 +605,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT, alpha: 0, text: "", - backgroundColor: { red: 51, green: 51, blue: 51 }, + backgroundColor: { + red: 51, + green: 51, + blue: 51 + }, backgroundAlpha: 1.0, visible: false, }); @@ -556,7 +617,11 @@ CameraTool = function(cameraManager) { var defaultCubeProps = { size: ORIENTATION_OVERLAY_CUBE_SIZE, alpha: 1, - color: { red: 255, green: 0, blue: 0 }, + color: { + red: 255, + green: 0, + blue: 0 + }, solid: true, visible: true, drawOnHUD: true, @@ -564,10 +629,26 @@ CameraTool = function(cameraManager) { var defaultLineProps = { lineWidth: 1.5, alpha: 1, - position: { x: 0, y: 0, z: 0 }, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, visible: false, drawOnHUD: true, }; @@ -582,30 +663,66 @@ CameraTool = function(cameraManager) { var OOHS = ORIENTATION_OVERLAY_HALF_SIZE; var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: -OOHS, y: OOHS, z: OOHS }, + position: { + x: -OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: -OOHS, z: OOHS }, + position: { + x: OOHS, + y: -OOHS, + z: OOHS + }, color: GREEN, })); var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: OOHS, z: -OOHS }, + position: { + x: OOHS, + y: OOHS, + z: -OOHS + }, color: BLUE, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: -OOHS, y: OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: -OOHS, + y: OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: -OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: -OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: GREEN, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: OOHS, z: -OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: OOHS, + z: -OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: BLUE, })); @@ -645,7 +762,10 @@ CameraTool = function(cameraManager) { } that.mousePressEvent = function(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (clickedOverlay == cubeX) { targetPitch = 0; @@ -666,12 +786,18 @@ CameraTool = function(cameraManager) { }; that.setVisible = function(visible) { - orientationOverlay.setProperties({ visible: visible }); - Overlays.editOverlay(background, { visible: visible }); - Overlays.editOverlay(backgroundBorder, { visible: visible }); + orientationOverlay.setProperties({ + visible: visible + }); + Overlays.editOverlay(background, { + visible: visible + }); + Overlays.editOverlay(backgroundBorder, { + visible: visible + }); }; that.setVisible(false); return that; -}; +}; \ No newline at end of file From 51acf07c153d27c5fce95f6d6198897e1dd0c49b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 14:50:42 -0700 Subject: [PATCH 21/57] Factor couple methods in AssetUtils --- interface/src/Application.cpp | 2 +- interface/src/ui/AssetUploadDialogFactory.cpp | 13 ++-- libraries/networking/src/AssetRequest.cpp | 52 ++------------ libraries/networking/src/AssetRequest.h | 3 - libraries/networking/src/AssetUtils.cpp | 67 +++++++++++++++++++ libraries/networking/src/AssetUtils.h | 12 ++-- 6 files changed, 87 insertions(+), 62 deletions(-) create mode 100644 libraries/networking/src/AssetUtils.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9e9830d977..a553eb4e9e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4118,7 +4118,7 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) EntityItemProperties properties; properties.setType(EntityTypes::Model); - properties.setModelURL(QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension())); + properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension())); properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f); properties.setName(QUrl(upload->getFilename()).fileName()); diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 4910e6d604..e169d88c8c 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -11,11 +11,6 @@ #include "AssetUploadDialogFactory.h" -#include -#include -#include -#include - #include #include #include @@ -24,6 +19,12 @@ #include #include +#include +#include +#include +#include +#include + AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { static AssetUploadDialogFactory staticInstance; return staticInstance; @@ -85,7 +86,7 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // setup the line edit to hold the copiable text QLineEdit* lineEdit = new QLineEdit; - QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); // set the ATP URL as the text value so it's copiable lineEdit->insert(atpURL); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index b4e3882a5e..a0a3ca5339 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -14,10 +14,8 @@ #include #include -#include #include "AssetClient.h" -#include "NetworkAccessManager.h" #include "NetworkLogging.h" #include "NodeList.h" #include "ResourceCache.h" @@ -41,7 +39,8 @@ void AssetRequest::start() { } // Try to load from cache - if (loadFromCache()) { + _data = loadFromCache(getUrl()); + if (!_data.isNull()) { _info.hash = _hash; _info.size = _data.size(); _error = NoError; @@ -112,7 +111,7 @@ void AssetRequest::start() { _totalReceived += data.size(); emit progress(_totalReceived, _info.size); - if (saveToCache(data)) { + if (saveToCache(getUrl(), data)) { qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache"; } } else { @@ -133,49 +132,6 @@ void AssetRequest::start() { } QUrl AssetRequest::getUrl() const { - if (!_extension.isEmpty()) { - return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, _hash, _extension)); - } else { - return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, _hash)); - } -} - -bool AssetRequest::loadFromCache() { - if (auto cache = NetworkAccessManager::getInstance().cache()) { - auto url = getUrl(); - if (auto ioDevice = cache->data(url)) { - _data = ioDevice->readAll(); - return true; - } else { - qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; - } - } else { - qCWarning(asset_client) << "No disk cache to load assets from."; - } - return false; -} - -bool AssetRequest::saveToCache(const QByteArray& file) const { - if (auto cache = NetworkAccessManager::getInstance().cache()) { - auto url = getUrl(); - - if (!cache->metaData(url).isValid()) { - QNetworkCacheMetaData metaData; - metaData.setUrl(url); - metaData.setSaveToDisk(true); - metaData.setLastModified(QDateTime::currentDateTime()); - metaData.setExpirationDate(QDateTime()); // Never expires - - if (auto ioDevice = cache->prepare(metaData)) { - ioDevice->write(file); - cache->insert(ioDevice); - return true; - } - qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; - } - } else { - qCWarning(asset_client) << "No disk cache to save assets to."; - } - return false; + return ::getUrl(_hash, _extension); } diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index 75e2353425..a5275e718a 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -53,9 +53,6 @@ signals: void progress(qint64 totalReceived, qint64 total); private: - bool loadFromCache(); - bool saveToCache(const QByteArray& file) const; - State _state = NotStarted; Error _error = NoError; AssetInfo _info; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp new file mode 100644 index 0000000000..0cf45c3a4f --- /dev/null +++ b/libraries/networking/src/AssetUtils.cpp @@ -0,0 +1,67 @@ +// +// AssetUtils.h +// libraries/networking/src +// +// Created by Clément Brisset on 10/12/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AssetUtils.h" + +#include +#include + +#include "NetworkAccessManager.h" +#include "NetworkLogging.h" + +#include "ResourceManager.h" + +QUrl getUrl(const QString& hash, const QString& extension) { + if (!extension.isEmpty()) { + return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension)); + } else { + return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, hash)); + } +} + +QByteArray hashData(const QByteArray& data) { + return QCryptographicHash::hash(data, QCryptographicHash::Sha256); +} + +QByteArray loadFromCache(const QUrl& url) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + if (auto ioDevice = cache->data(url)) { + return ioDevice->readAll(); + } else { + qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; + } + } else { + qCWarning(asset_client) << "No disk cache to load assets from."; + } + return QByteArray(); +} + +bool saveToCache(const QUrl& url, const QByteArray& file) { + if (auto cache = NetworkAccessManager::getInstance().cache()) { + if (!cache->metaData(url).isValid()) { + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + metaData.setSaveToDisk(true); + metaData.setLastModified(QDateTime::currentDateTime()); + metaData.setExpirationDate(QDateTime()); // Never expires + + if (auto ioDevice = cache->prepare(metaData)) { + ioDevice->write(file); + cache->insert(ioDevice); + return true; + } + qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; + } + } else { + qCWarning(asset_client) << "No disk cache to save assets to."; + } + return false; +} \ No newline at end of file diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index b18ff329fc..5fd5c9144d 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -12,10 +12,11 @@ #ifndef hifi_AssetUtils_h #define hifi_AssetUtils_h -#include - #include +#include +#include + using MessageID = uint32_t; using DataOffset = int64_t; @@ -31,8 +32,11 @@ enum AssetServerError : uint8_t { PermissionDenied }; -const QString ATP_SCHEME = "atp"; +QUrl getUrl(const QString& hash, const QString& extension = QString()); -inline QByteArray hashData(const QByteArray& data) { return QCryptographicHash::hash(data, QCryptographicHash::Sha256); } +QByteArray hashData(const QByteArray& data); + +QByteArray loadFromCache(const QUrl& url); +bool saveToCache(const QUrl& url, const QByteArray& file); #endif From 4abac35c721198556c9d3c293774b58b808f45ef Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 14:51:28 -0700 Subject: [PATCH 22/57] Cache on upload --- libraries/networking/src/AssetUpload.cpp | 16 ++++++++++++---- libraries/networking/src/AssetUpload.h | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 92632a43e5..67c079d248 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -23,9 +23,9 @@ AssetUpload::AssetUpload(QObject* object, const QString& filename) : } -void AssetUpload::start() { +void AssetUpload::start(bool cacheOnSuccess) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); + QMetaObject::invokeMethod(this, "start", Q_ARG(bool, cacheOnSuccess)); return; } @@ -37,14 +37,16 @@ void AssetUpload::start() { // file opened, read the data and grab the extension _extension = QFileInfo(_filename).suffix(); - auto data = file.readAll(); + _data = file.readAll(); // ask the AssetClient to upload the asset and emit the proper signals from the passed callback auto assetClient = DependencyManager::get(); qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ + assetClient->uploadAsset(_data, _extension, + [this, cacheOnSuccess](bool responseReceived, AssetServerError error, + const QString& hash){ if (!responseReceived) { _error = NetworkError; } else { @@ -63,6 +65,12 @@ void AssetUpload::start() { break; } } + + if (cacheOnSuccess && _error == NoError && + hash == hashData(_data).toHex()) { + saveToCache(getUrl(hash, _extension), _data); + } + emit finished(this, hash); }); } else { diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 80faa8a9c1..fbba545209 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -37,7 +37,7 @@ public: AssetUpload(QObject* parent, const QString& filename); - Q_INVOKABLE void start(); + Q_INVOKABLE void start(bool cacheOnSuccess = true); const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } @@ -50,6 +50,7 @@ signals: private: QString _filename; QString _extension; + QByteArray _data; Error _error; }; From 0cdad785811cf0c41927408d8f66279ca54b9b0e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 14:58:44 -0700 Subject: [PATCH 23/57] Move debug to helper functions --- libraries/networking/src/AssetRequest.cpp | 5 +---- libraries/networking/src/AssetUtils.cpp | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index a0a3ca5339..121b4cd4fd 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -47,7 +47,6 @@ void AssetRequest::start() { _state = Finished; emit finished(this); - qCDebug(asset_client) << getUrl().toDisplayString() << "loaded from disk cache."; return; } @@ -111,9 +110,7 @@ void AssetRequest::start() { _totalReceived += data.size(); emit progress(_totalReceived, _info.size); - if (saveToCache(getUrl(), data)) { - qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache"; - } + saveToCache(getUrl(), data); } else { // hash doesn't match - we have an error _error = HashVerificationFailed; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 0cf45c3a4f..7311d73525 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -34,6 +34,7 @@ QByteArray hashData(const QByteArray& data) { QByteArray loadFromCache(const QUrl& url) { if (auto cache = NetworkAccessManager::getInstance().cache()) { if (auto ioDevice = cache->data(url)) { + qCDebug(asset_client) << url.toDisplayString() << "loaded from disk cache."; return ioDevice->readAll(); } else { qCDebug(asset_client) << url.toDisplayString() << "not in disk cache"; @@ -56,6 +57,7 @@ bool saveToCache(const QUrl& url, const QByteArray& file) { if (auto ioDevice = cache->prepare(metaData)) { ioDevice->write(file); cache->insert(ioDevice); + qCDebug(asset_client) << url.toDisplayString() << "saved to disk cache"; return true; } qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache."; From cfba5752cecf9ecf8607ade2514d20bc51b8825c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 15:02:53 -0700 Subject: [PATCH 24/57] Change checkbox text --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a553eb4e9e..9a6e2fff4d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4079,7 +4079,7 @@ bool Application::askToUploadAsset(const QString& filename) { // Option to drop model in world for models if (filename.endsWith(FBX_EXTENSION) || filename.endsWith(OBJ_EXTENSION)) { auto checkBox = new QCheckBox(&messageBox); - checkBox->setText("Drop model in world."); + checkBox->setText("Add to scene"); messageBox.setCheckBox(checkBox); } From 0e0a8301eb09d12a59ef60fcd71dbc074dd3e3fb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 15:06:51 -0700 Subject: [PATCH 25/57] Collapse line --- libraries/networking/src/AssetUpload.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 67c079d248..fd9ae7ab43 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -66,8 +66,7 @@ void AssetUpload::start(bool cacheOnSuccess) { } } - if (cacheOnSuccess && _error == NoError && - hash == hashData(_data).toHex()) { + if (cacheOnSuccess && _error == NoError && hash == hashData(_data).toHex()) { saveToCache(getUrl(hash, _extension), _data); } From 7eadb11404a33919023d3bcb7587cf4210b32b64 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 15:09:17 -0700 Subject: [PATCH 26/57] Remove optional upload caching --- libraries/networking/src/AssetUpload.cpp | 11 +++++------ libraries/networking/src/AssetUpload.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index fd9ae7ab43..6f4fcbf1f2 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -23,9 +23,9 @@ AssetUpload::AssetUpload(QObject* object, const QString& filename) : } -void AssetUpload::start(bool cacheOnSuccess) { +void AssetUpload::start() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "start", Q_ARG(bool, cacheOnSuccess)); + QMetaObject::invokeMethod(this, "start"); return; } @@ -44,9 +44,8 @@ void AssetUpload::start(bool cacheOnSuccess) { qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - assetClient->uploadAsset(_data, _extension, - [this, cacheOnSuccess](bool responseReceived, AssetServerError error, - const QString& hash){ + assetClient->uploadAsset(_data, _extension, [this](bool responseReceived, AssetServerError error, + const QString& hash){ if (!responseReceived) { _error = NetworkError; } else { @@ -66,7 +65,7 @@ void AssetUpload::start(bool cacheOnSuccess) { } } - if (cacheOnSuccess && _error == NoError && hash == hashData(_data).toHex()) { + if (_error == NoError && hash == hashData(_data).toHex()) { saveToCache(getUrl(hash, _extension), _data); } diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index fbba545209..4047a44923 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -37,7 +37,7 @@ public: AssetUpload(QObject* parent, const QString& filename); - Q_INVOKABLE void start(bool cacheOnSuccess = true); + Q_INVOKABLE void start(); const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } From b7ba99380ab087168b305abbed084a8fa323180e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 15:32:44 -0700 Subject: [PATCH 27/57] Pretty up loading debug --- libraries/networking/src/ResourceCache.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e2f57f97a3..84af87f217 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -319,10 +319,10 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { - qDebug() << "Finished loading:" << _url; + qDebug().noquote() << "Finished loading:" << _url.toDisplayString(); _loaded = true; } else { - qDebug() << "Failed to load:" << _url; + qDebug().noquote() << "Failed to load:" << _url.toDisplayString(); _failedToLoad = true; } _loadPriorities.clear(); @@ -339,13 +339,13 @@ void Resource::makeRequest() { _request = ResourceManager::createResourceRequest(this, _activeUrl); if (!_request) { - qDebug() << "Failed to get request for " << _url; + qDebug().noquote() << "Failed to get request for" << _url.toDisplayString(); ResourceCache::requestCompleted(this); finishedLoading(false); return; } - qDebug() << "Starting request for: " << _url; + qDebug().noquote() << "Starting request for:" << _url.toDisplayString(); connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); @@ -368,7 +368,8 @@ void Resource::handleReplyFinished() { auto result = _request->getResult(); if (result == ResourceRequest::Success) { _data = _request->getData(); - qDebug() << "Request finished for " << _url << ", " << _activeUrl; + auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); + qDebug().noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); finishedLoading(true); emit loaded(_data); From 6cbeb6e7c3a120b73601268c39da56bf6c691853 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 12 Oct 2015 15:40:16 -0700 Subject: [PATCH 28/57] Fix my bug, introduced in PR#6012, in which startup position goto got cancelled out. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..605a81e9d9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -152,7 +152,7 @@ void MyAvatar::reset() { setEnableRigAnimations(true); // Reset dynamic state. - _wasPushing = _isPushing = _isBraking = _billboardValid = _goToPending = _straighteningLean = false; + _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); From ed9907ce66a2832cddac36f66f106778811b0f39 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 12 Oct 2015 16:23:12 -0700 Subject: [PATCH 29/57] do avatar simulation before physics simulation so that physics actions can update based on avatar --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9e9830d977..739fefbc7f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2761,6 +2761,8 @@ void Application::update(float deltaTime) { updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present + _avatarUpdate->synchronousProcess(); + { PerformanceTimer perfTimer("physics"); myAvatar->relayDriveKeysToCharacterController(); @@ -2822,8 +2824,6 @@ void Application::update(float deltaTime) { _overlays.update(deltaTime); } - _avatarUpdate->synchronousProcess(); - // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera From 47471e837ff72a9c30812aa972d848bc85afea15 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:31:36 -0700 Subject: [PATCH 30/57] reset targets once they have stopped moving --- examples/toys/ping_pong_gun/createTargets.js | 15 ++++++++++++--- unpublishedScripts/hiddenEntityReset.js | 19 ++++++++++--------- unpublishedScripts/masterReset.js | 18 +++++++++--------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/examples/toys/ping_pong_gun/createTargets.js b/examples/toys/ping_pong_gun/createTargets.js index 22329f90f0..013150b241 100644 --- a/examples/toys/ping_pong_gun/createTargets.js +++ b/examples/toys/ping_pong_gun/createTargets.js @@ -16,8 +16,9 @@ var scriptURL = Script.resolvePath('wallTarget.js'); var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; +var MINIMUM_MOVE_LENGTH = 0.05; +var RESET_DISTANCE = 0.5; -var RESET_DISTANCE = 1; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; @@ -60,6 +61,8 @@ var targets = []; var originalPositions = []; +var lastPositions = []; + function addTargets() { var i; var row = -1; @@ -77,6 +80,7 @@ function addTargets() { position.y = startPosition.y - (row * VERTICAL_SPACING); originalPositions.push(position); + lastPositions.push(position); var targetProperties = { name: 'Target', @@ -103,7 +107,11 @@ function testTargetDistanceFromStart() { var distance = Vec3.subtract(originalPosition, currentPosition); var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -482,15 +488,10 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + script: scriptURL }; - var target = Entities.addEntity(targetProperties); - targets[index] = target; + + targets[index] = Entities.addEntity(targetProperties); } }); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index d9199a124d..e4db5fbf55 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -393,6 +393,7 @@ MasterReset = function() { var targets = []; var originalPositions = []; + var lastPositions = []; function addTargets() { var i; @@ -441,7 +442,11 @@ MasterReset = function() { var distance = Vec3.subtract(originalPosition, currentPosition); var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -455,15 +460,10 @@ MasterReset = function() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + script: scriptURL }; - var target = Entities.addEntity(targetProperties); - targets[index] = target; + + targets[index] = Entities.addEntity(targetProperties); } }); From bc4200d8d7b42eac3a5ebb422f394d66077093ea Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:35:06 -0700 Subject: [PATCH 31/57] push last positions to array --- unpublishedScripts/hiddenEntityReset.js | 5 +++-- unpublishedScripts/masterReset.js | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 5eff989701..bc9ecbcf22 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -438,8 +438,9 @@ var position = Vec3.sum(startPosition, multiplier); position.y = startPosition.y - (row * VERTICAL_SPACING); - originalPositions.push(position); - + originalPositions.push(position); + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index e4db5fbf55..1644688e5d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -411,7 +411,8 @@ MasterReset = function() { position.y = startPosition.y - (row * VERTICAL_SPACING); originalPositions.push(position); - + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', @@ -429,8 +430,10 @@ MasterReset = function() { } }) }; + var target = Entities.addEntity(targetProperties); targets.push(target); + } } From 60f5a0630822e5bd4c0fc0be2d536a6f90f03a23 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 12 Oct 2015 16:44:51 -0700 Subject: [PATCH 32/57] turn on the asset-server by default --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e3ab2c51b8..eb1c478489 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -176,7 +176,7 @@ "type": "checkbox", "label": "Enabled", "help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)", - "default": false, + "default": true, "advanced": true } ] From f0ce698d78042e0b5313264fc1d152d64d274f52 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 12 Oct 2015 16:57:20 -0700 Subject: [PATCH 33/57] switch back to springy grab --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 05dfc8e5d5..f4bc7496da 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -480,7 +480,7 @@ function MyController(hand, triggerAction) { var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, { + this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, From 02620c4b0dced96e067ffcedc39ebe51b0fe7f3f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:59:42 -0700 Subject: [PATCH 34/57] make targets ungrabbable --- examples/toys/ping_pong_gun/createTargets.js | 9 +- unpublishedScripts/hiddenEntityReset.js | 11 +- unpublishedScripts/masterReset.js | 2435 +++++++++--------- 3 files changed, 1233 insertions(+), 1222 deletions(-) diff --git a/examples/toys/ping_pong_gun/createTargets.js b/examples/toys/ping_pong_gun/createTargets.js index 013150b241..fb286b1928 100644 --- a/examples/toys/ping_pong_gun/createTargets.js +++ b/examples/toys/ping_pong_gun/createTargets.js @@ -111,7 +111,7 @@ function testTargetDistanceFromStart() { lastPositions[index] = currentPosition; - if (length > RESET_DISTANCE && moving RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -125,7 +125,12 @@ function testTargetDistanceFromStart() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL + script: scriptURL, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }; targets[index] = Entities.addEntity(targetProperties); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index bc9ecbcf22..f959923ad3 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -413,6 +413,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + grabbable: false } }) }); @@ -438,9 +441,9 @@ var position = Vec3.sum(startPosition, multiplier); position.y = startPosition.y - (row * VERTICAL_SPACING); - originalPositions.push(position); - lastPositions.push(position); - + originalPositions.push(position); + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', @@ -455,7 +458,7 @@ userData: JSON.stringify({ resetMe: { resetMe: true - } + }, }) }; var target = Entities.addEntity(targetProperties); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 1644688e5d..fdedac3ec3 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -26,1256 +26,1259 @@ var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTa MasterReset = function() { - var resetKey = "resetMe"; - var GRABBABLE_DATA_KEY = "grabbableKey"; + var resetKey = "resetMe"; + var GRABBABLE_DATA_KEY = "grabbableKey"; - var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - var shouldDeleteOnEndScript = false; + var shouldDeleteOnEndScript = false; - //Before creating anything, first search a radius and delete all the things that should be deleted - deleteAllToys(); - createAllToys(); + //Before creating anything, first search a radius and delete all the things that should be deleted + deleteAllToys(); + createAllToys(); - function createAllToys() { - createBlocks({ - x: 548.3, - y: 495.55, - z: 504.4 - }); - - createBasketBall({ - x: 547.73, - y: 495.5, - z: 505.47 - }); - - createDoll({ - x: 546.67, - y: 495.41, - z: 505.09 - }); - - createWand({ - x: 546.71, - y: 495.55, - z: 506.15 - }); - - createDice(); - - createFlashlight({ - x: 545.72, - y: 495.41, - z: 505.78 - }); - - - - createCombinedArmChair({ - x: 549.29, - y: 494.9, - z: 508.22 - }); - - createPottedPlant({ - x: 554.26, - y: 495.2, - z: 504.53 - }); - - createPingPongBallGun(); - - createBasketballHoop(); - createBasketballRack(); - - createGates(); - - createFire(); - // Handles toggling of all sconce lights - createLights(); - - - - createCat({ - x: 551.09, - y: 494.98, - z: 503.49 - }); - - - createSprayCan({ - x: 549.7, - y: 495.6, - z: 503.91 - }); - - createTargets(); - - } - - function deleteAllToys() { - var entities = Entities.findEntities(MyAvatar.position, 100); - - entities.forEach(function(entity) { - //params: customKey, id, defaultValue - var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; - if (shouldReset === true) { - Entities.deleteEntity(entity); - } - }); - } - - function createFire() { - - - var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); - - var animationSettings = JSON.stringify({ - fps: 30, - running: true, - loop: true, - firstFrame: 1, - lastFrame: 10000 - }); - - - var fire = Entities.addEntity({ - type: "ParticleEffect", - name: "fire", - animationSettings: animationSettings, - textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - position: { - x: 551.45, - y: 494.82, - z: 502.05 - }, - emitRate: 100, - colorStart: { - red: 70, - green: 70, - blue: 137 - }, - color: { - red: 200, - green: 99, - blue: 42 - }, - colorFinish: { - red: 255, - green: 99, - blue: 32 - }, - radiusSpread: 0.01, - radiusStart: 0.02, - radiusEnd: 0.001, - particleRadius: 0.05, - radiusFinish: 0.0, - emitOrientation: myOrientation, - emitSpeed: 0.3, - speedSpread: 0.1, - alphaStart: 0.05, - alpha: 0.1, - alphaFinish: 0.05, - emitDimensions: { - x: 1, - y: 1, - z: 0.1 - }, - polarFinish: 0.1, - emitAcceleration: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - accelerationSpread: { - x: 0.1, - y: 0.01, - z: 0.1 - }, - lifespan: 1, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - } - - function createBasketballRack() { - var NUMBER_OF_BALLS = 4; - var DIAMETER = 0.30; - var RESET_DISTANCE = 1; - var MINIMUM_MOVE_LENGTH = 0.05; - var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; - var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; - var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; - var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; - - var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); - - var rackStartPosition = { - x: 542.86, - y: 494.84, - z: 475.06 - }; - var rack = Entities.addEntity({ - name: 'Basketball Rack', - type: "Model", - modelURL: rackURL, - position: rackStartPosition, - rotation: rackRotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - linearDamping: 1, - dimensions: { - x: 0.4, - y: 1.37, - z: 1.73 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - compoundShapeURL: rackCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - - }) - }); - - var collidingBalls = []; - var originalBallPositions = []; - - function createCollidingBalls() { - var position = rackStartPosition; - - var i; - for (i = 0; i < NUMBER_OF_BALLS; i++) { - var ballPosition = { - x: position.x, - y: position.y + DIAMETER * 2, - z: position.z + (DIAMETER) - (DIAMETER * i) - }; - - var collidingBall = Entities.addEntity({ - type: "Model", - name: 'Colliding Basketball', - shapeType: 'Sphere', - position: { - x: position.x + (DIAMETER * 2) - (DIAMETER * i), - y: position.y + DIAMETER * 2, - z: position.z - }, - dimensions: { - x: DIAMETER, - y: DIAMETER, - z: DIAMETER - }, - restitution: 1.0, - linearDamping: 0.00001, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - modelURL: basketballURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + function createAllToys() { + createBlocks({ + x: 548.3, + y: 495.55, + z: 504.4 }); - collidingBalls.push(collidingBall); - originalBallPositions.push(position); - } + createBasketBall({ + x: 547.73, + y: 495.5, + z: 505.47 + }); + + createDoll({ + x: 546.67, + y: 495.41, + z: 505.09 + }); + + createWand({ + x: 546.71, + y: 495.55, + z: 506.15 + }); + + createDice(); + + createFlashlight({ + x: 545.72, + y: 495.41, + z: 505.78 + }); + + + + createCombinedArmChair({ + x: 549.29, + y: 494.9, + z: 508.22 + }); + + createPottedPlant({ + x: 554.26, + y: 495.2, + z: 504.53 + }); + + createPingPongBallGun(); + + createBasketballHoop(); + createBasketballRack(); + + createGates(); + + createFire(); + // Handles toggling of all sconce lights + createLights(); + + + + createCat({ + x: 551.09, + y: 494.98, + z: 503.49 + }); + + + createSprayCan({ + x: 549.7, + y: 495.6, + z: 503.91 + }); + + createTargets(); + } - function testBallDistanceFromStart() { - var resetCount = 0; + function deleteAllToys() { + var entities = Entities.findEntities(MyAvatar.position, 100); - collidingBalls.forEach(function(ball, index) { - var currentPosition = Entities.getEntityProperties(ball, "position").position; - var originalPosition = originalBallPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + entities.forEach(function(entity) { + //params: customKey, id, defaultValue + var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; + if (shouldReset === true) { + Entities.deleteEntity(entity); + } + }); + } - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(ball, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - resetCount++; - if (resetCount === NUMBER_OF_BALLS) { + function createFire() { + + + var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); + + var animationSettings = JSON.stringify({ + fps: 30, + running: true, + loop: true, + firstFrame: 1, + lastFrame: 10000 + }); + + + var fire = Entities.addEntity({ + type: "ParticleEffect", + name: "fire", + animationSettings: animationSettings, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + position: { + x: 551.45, + y: 494.82, + z: 502.05 + }, + emitRate: 100, + colorStart: { + red: 70, + green: 70, + blue: 137 + }, + color: { + red: 200, + green: 99, + blue: 42 + }, + colorFinish: { + red: 255, + green: 99, + blue: 32 + }, + radiusSpread: 0.01, + radiusStart: 0.02, + radiusEnd: 0.001, + particleRadius: 0.05, + radiusFinish: 0.0, + emitOrientation: myOrientation, + emitSpeed: 0.3, + speedSpread: 0.1, + alphaStart: 0.05, + alpha: 0.1, + alphaFinish: 0.05, + emitDimensions: { + x: 1, + y: 1, + z: 0.1 + }, + polarFinish: 0.1, + emitAcceleration: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + accelerationSpread: { + x: 0.1, + y: 0.01, + z: 0.1 + }, + lifespan: 1, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + } + + function createBasketballRack() { + var NUMBER_OF_BALLS = 4; + var DIAMETER = 0.30; + var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; + var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; + + var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); + + var rackStartPosition = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var rack = Entities.addEntity({ + name: 'Basketball Rack', + type: "Model", + modelURL: rackURL, + position: rackStartPosition, + rotation: rackRotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + linearDamping: 1, + dimensions: { + x: 0.4, + y: 1.37, + z: 1.73 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + compoundShapeURL: rackCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + + }) + }); + + var collidingBalls = []; + var originalBallPositions = []; + + function createCollidingBalls() { + var position = rackStartPosition; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Colliding Basketball', + shapeType: 'Sphere', + position: { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + + collidingBalls.push(collidingBall); + originalBallPositions.push(position); + } + } + + function testBallDistanceFromStart() { + var resetCount = 0; + + collidingBalls.forEach(function(ball, index) { + var currentPosition = Entities.getEntityProperties(ball, "position").position; + var originalPosition = originalBallPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(ball, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + resetCount++; + if (resetCount === NUMBER_OF_BALLS) { + deleteCollidingBalls(); + createCollidingBalls(); + } + } + }, 200); + } + }); + } + + function deleteEntity(entityID) { + if (entityID === rack) { deleteCollidingBalls(); - createCollidingBalls(); - } + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); } - }, 200); - } - }); - } - - function deleteEntity(entityID) { - if (entityID === rack) { - deleteCollidingBalls(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteCollidingBalls() { - while (collidingBalls.length > 0) { - Entities.deleteEntity(collidingBalls.pop()); - } - } - - createCollidingBalls(); - Entities.deletingEntity.connect(deleteEntity); - - var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); - } - - function createTargets() { - - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - - var RESET_DISTANCE = 1; - var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; - var NUMBER_OF_TARGETS = 6; - var TARGETS_PER_ROW = 3; - - var TARGET_DIMENSIONS = { - x: 0.06, - y: 0.42, - z: 0.42 - }; - - var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; - var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; - - - var startPosition = { - x: 548.68, - y: 497.30, - z: 509.74 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); - - var targetIntervalClearer = Entities.addEntity({ - name: 'Target Interval Clearer - delete me to clear', - type: 'Box', - position: startPosition, - dimensions: TARGET_DIMENSIONS, - rotation: rotation, - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - var targets = []; - - var originalPositions = []; - var lastPositions = []; - - function addTargets() { - var i; - var row = -1; - for (i = 0; i < NUMBER_OF_TARGETS; i++) { - - if (i % TARGETS_PER_ROW === 0) { - row++; } - var vHat = Quat.getFront(rotation); - var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); - var multiplier = Vec3.multiply(spacer, vHat); - var position = Vec3.sum(startPosition, multiplier); - position.y = startPosition.y - (row * VERTICAL_SPACING); - - originalPositions.push(position); - lastPositions.push(position); - - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: position, - rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true + function deleteCollidingBalls() { + while (collidingBalls.length > 0) { + Entities.deleteEntity(collidingBalls.pop()); } - }) + } + + createCollidingBalls(); + Entities.deletingEntity.connect(deleteEntity); + + var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); + } + + function createTargets() { + + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; + + var RESET_DISTANCE = 1; + var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; + var NUMBER_OF_TARGETS = 6; + var TARGETS_PER_ROW = 3; + + var TARGET_DIMENSIONS = { + x: 0.06, + y: 0.42, + z: 0.42 }; - var target = Entities.addEntity(targetProperties); - targets.push(target); + var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; + var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; + + + var startPosition = { + x: 548.68, + y: 497.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); + + var targetIntervalClearer = Entities.addEntity({ + name: 'Target Interval Clearer - delete me to clear', + type: 'Box', + position: startPosition, + dimensions: TARGET_DIMENSIONS, + rotation: rotation, + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + + var targets = []; + + var originalPositions = []; + var lastPositions = []; + + function addTargets() { + var i; + var row = -1; + for (i = 0; i < NUMBER_OF_TARGETS; i++) { + + if (i % TARGETS_PER_ROW === 0) { + row++; + } + + var vHat = Quat.getFront(rotation); + var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); + var multiplier = Vec3.multiply(spacer, vHat); + var position = Vec3.sum(startPosition, multiplier); + position.y = startPosition.y - (row * VERTICAL_SPACING); + + originalPositions.push(position); + lastPositions.push(position); + + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: position, + rotation: rotation, + script: targetsScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }; + + var target = Entities.addEntity(targetProperties); + targets.push(target); + + } + } + + function testTargetDistanceFromStart() { + targets.forEach(function(target, index) { + + var currentPosition = Entities.getEntityProperties(target, "position").position; + var originalPosition = originalPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + + Entities.deleteEntity(target); + + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: originalPositions[index], + rotation: rotation, + script: scriptURL + }; + + targets[index] = Entities.addEntity(targetProperties); + + } + }); + } + + + function deleteEntity(entityID) { + if (entityID === targetIntervalClearer) { + deleteTargets(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } + } + + function deleteTargets() { + while (targets.length > 0) { + Entities.deleteEntity(targets.pop()); + } + Entities.deleteEntity(targetIntervalClearer); + } + + Entities.deletingEntity.connect(deleteEntity); + var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); + + addTargets(); - } } - function testTargetDistanceFromStart() { - targets.forEach(function(target, index) { + function createCat(position) { - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true, + }); + var cat = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "cat", + script: catScriptURL, + animationURL: animationURL, + animationSettings: animationSettings, + position: position, + rotation: { + w: 0.35020983219146729, + x: -4.57763671875e-05, + y: 0.93664455413818359, + z: -1.52587890625e-05 + }, + dimensions: { + x: 0.15723302960395813, + y: 0.50762706995010376, + z: 0.90716040134429932 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + } - lastPositions[index] = currentPosition; + function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + var flashlight = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "flashlight", + script: flashlightScriptURL, + position: position, + dimensions: { + x: 0.08, + y: 0.30, + z: 0.08 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: 'box', + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); - Entities.deleteEntity(target); - var targetProperties = { - name: 'Target', + } + + function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; + + + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); + + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); + + + + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, + + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + } + + + + function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, + y: 494.96, + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }; + var dice1 = Entities.addEntity(diceProps); + + diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 + }; + + var dice2 = Entities.addEntity(diceProps); + + } + + + function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', type: 'Model', modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ + type: "Model", + modelURL: MODEL_URL, + shapeType: 'box', + script: pingPongScriptURL, + position: position, + rotation: rotation, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.08, + y: 0.21, + z: 0.47 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + } + + function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); + + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; + + var entity = Entities.addEntity({ + name: 'Bubble Wand', + type: "Model", + modelURL: WAND_MODEL, + position: position, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.05, + y: 0.25, + z: 0.05 + }, + //must be enabled to be grabbable in the physics engine shapeType: 'compound', collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: scriptURL - }; - - targets[index] = Entities.addEntity(targetProperties); - - } - }); - } - - - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - - } - - function createCat(position) { - - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "cat", - script: catScriptURL, - animationURL: animationURL, - animationSettings: animationSettings, - position: position, - rotation: { - w: 0.35020983219146729, - x: -4.57763671875e-05, - y: 0.93664455413818359, - z: -1.52587890625e-05 - }, - dimensions: { - x: 0.15723302960395813, - y: 0.50762706995010376, - z: 0.90716040134429932 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - } - - function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - - var flashlight = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "flashlight", - script: flashlightScriptURL, - position: position, - dimensions: { - x: 0.08, - y: 0.30, - z: 0.08 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: 'box', - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - - } - - function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - - - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); - - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); - - - - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, - - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - } - - - - function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }; - var dice1 = Entities.addEntity(diceProps); - - diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 - }; - - var dice2 = Entities.addEntity(diceProps); - - } - - - function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; - - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { - x: 0, - y: -100, - z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); - - var pingPongGun = Entities.addEntity({ - type: "Model", - modelURL: MODEL_URL, - shapeType: 'box', - script: pingPongScriptURL, - position: position, - rotation: rotation, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.08, - y: 0.21, - z: 0.47 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - } - - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - - var entity = Entities.addEntity({ - name: 'Bubble Wand', - type: "Model", - modelURL: WAND_MODEL, - position: position, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.05, - y: 0.25, - z: 0.05 - }, - //must be enabled to be grabbable in the physics engine - shapeType: 'compound', - collisionsWillMove: true, - compoundShapeURL: WAND_COLLISION_SHAPE, - //Look into why bubble wand is going through table when gravity is enabled - // gravity: {x: 0, y: -3.5, z: 0}, - // velocity: {x: 0, y: -0.01, z:0}, - script: wandScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - - } - - function createBasketBall(position) { - - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: position, - collisionsWillMove: true, - shapeType: "sphere", - name: "basketball", - dimensions: { - x: 0.25, - y: 0.26, - z: 0.25 - }, - gravity: { - x: 0, - y: -7, - z: 0 - }, - restitution: 10, - linearDamping: 0.0, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ - type: "Model", - name: "doll", - modelURL: modelURL, - script: dollScriptURL, - position: position, - shapeType: 'box', - dimensions: desiredDimensions, - gravity: { - x: 0, - y: -5, - z: 0 - }, - velocity: { - x: 0, - y: -0.1, - z: 0 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createSprayCan(position) { - - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "spraycan", - script: sprayPaintScriptURL, - modelURL: modelURL, - position: position, - dimensions: { - x: 0.07, - y: 0.17, - z: 0.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -0.5, - z: 0 - }, - velocity: { - x: 0, - y: -1, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - - function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) + compoundShapeURL: WAND_COLLISION_SHAPE, + //Look into why bubble wand is going through table when gravity is enabled + // gravity: {x: 0, y: -3.5, z: 0}, + // velocity: {x: 0, y: -0.01, z:0}, + script: wandScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) }); - } + } - } - function cleanup() { - deleteAllToys(); - } + function createBasketBall(position) { - if (shouldDeleteOnEndScript) { + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - Script.scriptEnding.connect(cleanup); - } + var entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: position, + collisionsWillMove: true, + shapeType: "sphere", + name: "basketball", + dimensions: { + x: 0.25, + y: 0.26, + z: 0.25 + }, + gravity: { + x: 0, + y: -7, + z: 0 + }, + restitution: 10, + linearDamping: 0.0, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; + + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ + type: "Model", + name: "doll", + modelURL: modelURL, + script: dollScriptURL, + position: position, + shapeType: 'box', + dimensions: desiredDimensions, + gravity: { + x: 0, + y: -5, + z: 0 + }, + velocity: { + x: 0, + y: -0.1, + z: 0 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createSprayCan(position) { + + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "spraycan", + script: sprayPaintScriptURL, + modelURL: modelURL, + position: position, + dimensions: { + x: 0.07, + y: 0.17, + z: 0.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -0.5, + z: 0 + }, + velocity: { + x: 0, + y: -1, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + + function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + + } + + function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + }, ]; + + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + } + } + + function cleanup() { + deleteAllToys(); + } + + if (shouldDeleteOnEndScript) { + + Script.scriptEnding.connect(cleanup); + } }; \ No newline at end of file From fc8a4f4f2dba746f0883e606dae60ef8dcd114e3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 17:00:49 -0700 Subject: [PATCH 35/57] cleanup --- unpublishedScripts/masterReset.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index fdedac3ec3..03dacef0a4 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -247,7 +247,6 @@ MasterReset = function() { grabbableKey: { grabbable: false } - }) }); From 0f998e81afd9f123083639e2d986220c689ba5fc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 17:03:32 -0700 Subject: [PATCH 36/57] add min reset distance --- unpublishedScripts/hiddenEntityReset.js | 4 ++-- unpublishedScripts/masterReset.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index f959923ad3..702aaec378 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -378,7 +378,8 @@ var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; @@ -392,7 +393,6 @@ var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; - var startPosition = { x: 548.68, y: 497.30, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 03dacef0a4..a6c3bfdd16 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -350,7 +350,8 @@ MasterReset = function() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; From 6a8ff676ed3c979d3c441ad72405a35f7bba8cac Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 12 Oct 2015 17:32:18 -0700 Subject: [PATCH 37/57] Remove overlay hiding from middle mouse button. Because it conflicts with edit.js camera panning. You can still toggle the overlay via the O key. --- interface/src/Application.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9a6e2fff4d..8705c7b61d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1890,16 +1890,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { computePickRay(mappedEvent.x(), mappedEvent.y())); sendEvent(this, &actionEvent); - } else if (event->button() == Qt::RightButton) { - // "right click" on controllers to toggle the overlay - if (deviceID > 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } - } else if (event->button() == Qt::MiddleButton) { - // mouse middle click to toggle the overlay - if (deviceID == 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } } } } From 309cde7f28735d2a23ac7f3943e7f35ea6513ed1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 13 Oct 2015 10:31:36 -0700 Subject: [PATCH 38/57] invert solidity of most toybox objects while held --- unpublishedScripts/hiddenEntityReset.js | 41 +- unpublishedScripts/masterReset.js | 1248 ++++++++++++----------- 2 files changed, 686 insertions(+), 603 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 702aaec378..1b3dd07e5e 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -319,6 +319,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -459,6 +462,9 @@ resetMe: { resetMe: true }, + grabbableKey: { + grabbable: false + } }) }; var target = Entities.addEntity(targetProperties); @@ -492,7 +498,15 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL + script: scriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) }; targets[index] = Entities.addEntity(targetProperties); @@ -553,6 +567,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + grabbable: false } }) }); @@ -588,7 +605,11 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } + }) }); @@ -856,6 +877,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }; @@ -945,6 +969,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1019,6 +1046,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1058,6 +1088,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1095,6 +1128,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1131,6 +1167,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a6c3bfdd16..5eaa35372f 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -291,6 +291,9 @@ MasterReset = function() { userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbable: { + invertSolidWhileHeld: true } }) }); @@ -443,69 +446,78 @@ MasterReset = function() { function testTargetDistanceFromStart() { targets.forEach(function(target, index) { - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + var currentPosition = Entities.getEntityProperties(target, "position").position; + var originalPosition = originalPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); - lastPositions[index] = currentPosition; + lastPositions[index] = currentPosition; - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { - Entities.deleteEntity(target); + Entities.deleteEntity(target); - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: scriptURL + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: originalPositions[index], + rotation: rotation, + script: scriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + } + }) }; targets[index] = Entities.addEntity(targetProperties); } }); - } - - - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - } - function createCat(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ + function deleteEntity(entityID) { + if (entityID === targetIntervalClearer) { + deleteTargets(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } + } + + function deleteTargets() { + while (targets.length > 0) { + Entities.deleteEntity(targets.pop()); + } + Entities.deleteEntity(targetIntervalClearer); + } + + Entities.deletingEntity.connect(deleteEntity); + var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); + + addTargets(); + +} + +function createCat(position) { + + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true, + }); + var cat = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "cat", @@ -525,18 +537,22 @@ MasterReset = function() { z: 0.90716040134429932 }, userData: JSON.stringify({ - resetMe: { - resetMe: true + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } } }) - }); + }); - } +} - function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; +function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - var flashlight = Entities.addEntity({ + var flashlight = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "flashlight", @@ -560,345 +576,353 @@ MasterReset = function() { }, shapeType: 'box', userData: JSON.stringify({ - resetMe: { - resetMe: true + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; +function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - } +} - function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }; - var dice1 = Entities.addEntity(diceProps); - - diceProps.position = { - x: 541.05, +function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, y: 494.96, - z: 509.0 - }; - - var dice2 = Entities.addEntity(diceProps); - - } - - - function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; - - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { - x: 0, - y: -100, - z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ resetMe: { resetMe: true, }, grabbableKey: { - grabbable: false + invertSolidWhileHeld: true } - }) - }); - } + } + }) +}; +var dice1 = Entities.addEntity(diceProps); - function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; +diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 +}; - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; +var dice2 = Entities.addEntity(diceProps); - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); +} - var pingPongGun = Entities.addEntity({ + +function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); +} + +function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, shapeType: 'box', @@ -917,57 +941,61 @@ MasterReset = function() { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); - } + }); +} - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); +function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } + grabbableKey: { + grabbable: false + } + }) + }); +} - function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; +function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ name: 'Bubble Wand', type: "Model", modelURL: WAND_MODEL, @@ -991,20 +1019,24 @@ MasterReset = function() { // velocity: {x: 0, y: -0.01, z:0}, script: wandScriptURL, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createBasketBall(position) { +function createBasketBall(position) { - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", modelURL: modelURL, position: position, @@ -1030,24 +1062,28 @@ MasterReset = function() { }, collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; +function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ type: "Model", name: "doll", modelURL: modelURL, @@ -1067,19 +1103,23 @@ MasterReset = function() { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createSprayCan(position) { +function createSprayCan(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", name: "spraycan", script: sprayPaintScriptURL, @@ -1103,182 +1143,186 @@ MasterReset = function() { z: 0 }, userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - - function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 + resetMe: { + resetMe: true, }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); + grabbableKey: { + invertSolidWhileHeld: true + } + } + }) + }); +} + +function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false } + }) + }); +} + + +function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + +} + +function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + }, ]; + + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + } } +} - function cleanup() { - deleteAllToys(); - } +function cleanup() { + deleteAllToys(); +} - if (shouldDeleteOnEndScript) { +if (shouldDeleteOnEndScript) { - Script.scriptEnding.connect(cleanup); - } + Script.scriptEnding.connect(cleanup); +} }; \ No newline at end of file From cf74cfb50e48ae6b5bafeb2e397cb25fda31e3d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:38:52 -0700 Subject: [PATCH 39/57] add an ATPAssetMigrator for bulk ATP migration --- interface/src/Menu.cpp | 10 +- interface/src/Menu.h | 1 + interface/src/assets/ATPAssetMigrator.cpp | 177 ++++++++++++++++++ interface/src/assets/ATPAssetMigrator.h | 48 +++++ interface/src/ui/AssetUploadDialogFactory.cpp | 51 +++-- interface/src/ui/AssetUploadDialogFactory.h | 3 +- libraries/networking/src/AssetClient.cpp | 66 ++++--- libraries/networking/src/AssetClient.h | 1 + libraries/networking/src/AssetUpload.cpp | 117 ++++++++---- libraries/networking/src/AssetUpload.h | 7 +- 10 files changed, 385 insertions(+), 96 deletions(-) create mode 100644 interface/src/assets/ATPAssetMigrator.cpp create mode 100644 interface/src/assets/ATPAssetMigrator.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae41ca2493..cc9b259e06 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -24,6 +24,7 @@ #include "Application.h" #include "AccountManager.h" +#include "assets/ATPAssetMigrator.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "devices/DdeFaceTracker.h" @@ -354,7 +355,7 @@ Menu::Menu() { MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& assetDialogFactory = AssetUploadDialogFactory::getInstance(); - assetDialogFactory.setParent(this); + assetDialogFactory.setDialogParent(this); QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::UploadAsset, @@ -365,6 +366,13 @@ Menu::Menu() { // disable the asset upload action by default - it gets enabled only if asset server becomes present assetUpload->setEnabled(false); + auto& atpMigrator = ATPAssetMigrator::getInstance(); + atpMigrator.setDialogParent(this); + + QAction* assetMigration = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, + 0, &atpMigrator, + SLOT(loadEntityServerFile())); + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4c9c3ef7b5..43388fcbd1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -135,6 +135,7 @@ namespace MenuOption { const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose"; const QString Antialiasing = "Antialiasing"; + const QString AssetMigration = "ATP Asset Migration"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp new file mode 100644 index 0000000000..d32378085b --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -0,0 +1,177 @@ +// +// ATPAssetMigrator.cpp +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ATPAssetMigrator.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "../ui/AssetUploadDialogFactory.h" + +ATPAssetMigrator& ATPAssetMigrator::getInstance() { + static ATPAssetMigrator instance; + return instance; +} + +static const QString MODEL_URL_KEY = "modelURL"; + +void ATPAssetMigrator::loadEntityServerFile() { + auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", + QString(), QString("Entity-Server Content (*.gz)")); + + if (!filename.isEmpty()) { + qDebug() << "Selected filename for ATP asset migration: " << filename; + + // try to open the file at the given filename + QFile modelsFile { filename }; + + if (modelsFile.open(QIODevice::ReadOnly)) { + QByteArray compressedJsonData = modelsFile.readAll(); + QByteArray jsonData; + + if (!gunzip(compressedJsonData, jsonData)) { + QMessageBox::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); + } + + QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); + _entitiesArray = modelsJSON.object()["Entities"].toArray(); + + for (auto jsonValue : _entitiesArray) { + QJsonObject entityObject = jsonValue.toObject(); + QString modelURLString = entityObject.value(MODEL_URL_KEY).toString(); + + if (!modelURLString.isEmpty()) { + QUrl modelURL = QUrl(modelURLString); + + if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + + QMessageBox messageBox; + messageBox.setWindowTitle("Asset Migration"); + messageBox.setText("Would you like to migrate the following file to the asset server?"); + messageBox.setInformativeText(modelURL.toDisplayString()); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Ok); + + if (messageBox.exec() == QMessageBox::Ok) { + // user wants to migrate this asset + + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else { + auto request = ResourceManager::createResourceRequest(this, modelURL); + + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + request->send(); + } + + } + } + } + } + + _doneReading = true; + + } else { + QMessageBox::warning(_dialogParent, "Error", + "There was a problem loading that entity-server file for ATP asset migration. Please try again"); + } + } +} + +void ATPAssetMigrator::migrateResource(ResourceRequest* request) { + // use an asset client to upload the asset + auto assetClient = DependencyManager::get(); + + QFileInfo assetInfo { request->getUrl().fileName() }; + + auto upload = assetClient->createUpload(request->getData(), assetInfo.completeSuffix()); + + if (upload) { + // add this URL to our hash of AssetUpload to original URL + _originalURLs.insert(upload, request->getUrl()); + + qDebug() << "Starting upload of asset from" << request->getUrl(); + + // connect to the finished signal so we know when the AssetUpload is done + QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); + + // start the upload now + upload->start(); + } else { + // show a QMessageBox to say that there is no local asset server + QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \ + " to a local asset-server.").arg(assetInfo.fileName()); + + QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText); + } +} + +void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& hash) { + if (upload->getError() == AssetUpload::NoError) { + + const auto& modelURL = _originalURLs[upload]; + + // successfully uploaded asset - make any required replacements found in the pending replacements + auto values = _pendingReplacements.values(modelURL); + + QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + + for (auto value : values) { + // replace the modelURL in this QJsonValueRef with the hash + QJsonObject valueObject = value.toObject(); + valueObject[MODEL_URL_KEY] = atpURL; + value = valueObject; + } + + // pull the replaced models from _pendingReplacements + _pendingReplacements.remove(modelURL); + + // are we out of pending replacements? if so it is time to save the entity-server file + if (_doneReading && _pendingReplacements.empty()) { + // show a dialog to ask the user where they want to save the file + } + } else { + AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h new file mode 100644 index 0000000000..84f05b600d --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.h @@ -0,0 +1,48 @@ +// +// ATPAssetMigrator.h +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_ATPAssetMigrator_h +#define hifi_ATPAssetMigrator_h + +#include +#include +#include + +class AssetUpload; +class ResourceRequest; + +class ATPAssetMigrator : public QObject { + Q_OBJECT +public: + static ATPAssetMigrator& getInstance(); + + void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } +public slots: + void loadEntityServerFile(); +private slots: + void assetUploadFinished(AssetUpload* upload, const QString& hash); +private: + void migrateResource(ResourceRequest* request); + + QWidget* _dialogParent = nullptr; + QJsonArray _entitiesArray; + + bool _doneReading { false }; + + QMultiHash _pendingReplacements; + QHash _uploadedAssets; + QHash _originalURLs; +}; + + +#endif // hifi_ATPAssetMigrator_h diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 4910e6d604..7b1611b616 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -29,7 +29,7 @@ AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { return staticInstance; } -static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + void AssetUploadDialogFactory::showDialog() { auto nodeList = DependencyManager::get(); @@ -59,7 +59,7 @@ void AssetUploadDialogFactory::showDialog() { } } else { // we don't have permission to upload to asset server in this domain - show the permission denied error - showErrorDialog(QString(), PERMISSION_DENIED_ERROR); + showErrorDialog(nullptr, _dialogParent, AssetUpload::PERMISSION_DENIED_ERROR); } } @@ -117,42 +117,33 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // show the new dialog hashCopyDialog->show(); } else { - // figure out the right error message for the message box - QString additionalError; - - switch (upload->getError()) { - case AssetUpload::PermissionDenied: - additionalError = PERMISSION_DENIED_ERROR; - break; - case AssetUpload::TooLarge: - additionalError = "The uploaded content was too large and could not be stored in the asset-server."; - break; - case AssetUpload::FileOpenError: - additionalError = "The file could not be opened. Please check your permissions and try again."; - break; - case AssetUpload::NetworkError: - additionalError = "The file could not be opened. Please check your network connectivity."; - break; - default: - // not handled, do not show a message box - return; - } - // display a message box with the error - showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError); + showErrorDialog(upload, _dialogParent); } upload->deleteLater(); } -void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) { - QString errorMessage; +void AssetUploadDialogFactory::showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage) { + QString filename; - if (!filename.isEmpty()) { - errorMessage += QString("Failed to upload %1.\n\n").arg(filename); + if (upload) { + filename = QFileInfo { upload->getFilename() }.fileName(); } - errorMessage += additionalError; + QString errorMessage = overrideMessage; - QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage); + if (errorMessage.isEmpty() && upload) { + errorMessage = upload->getErrorString(); + } + + QString dialogMessage; + + if (upload) { + dialogMessage += QString("Failed to upload %1.\n\n").arg(filename); + } + + dialogMessage += errorMessage; + + QMessageBox::warning(dialogParent, "Failed Upload", dialogMessage); } diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index a8e8765f20..fe3ecf5dc4 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -25,6 +25,7 @@ public: AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete; static AssetUploadDialogFactory& getInstance(); + static void showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage = QString()); void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } @@ -35,7 +36,7 @@ public slots: private: AssetUploadDialogFactory() = default; - void showErrorDialog(const QString& filename, const QString& additionalError); + QWidget* _dialogParent { nullptr }; }; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 87af2a5cf8..b7f1205847 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -65,43 +65,65 @@ void AssetClient::init() { } } +bool haveAssetServer() { + auto nodeList = DependencyManager::get(); + SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + + if (!assetServer) { + qCWarning(asset_client) << "Could not complete AssetClient operation " + << "since you are not currently connected to an asset-server."; + return false; + } + + return true; +} + AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { qCWarning(asset_client) << "Invalid hash size"; return nullptr; } - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - - if (!assetServer) { - qCWarning(asset_client).nospace() << "Could not request " << hash << "." << extension - << " since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto request = new AssetRequest(hash, extension); + + // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) + request->moveToThread(thread()); + request->setParent(this); + + return request; + } else { return nullptr; } - - auto request = new AssetRequest(hash, extension); - - // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) - request->moveToThread(thread()); - - return request; } + + AssetUpload* AssetClient::createUpload(const QString& filename) { - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - if (!assetServer) { - qCWarning(asset_client) << "Could not upload" << filename << "since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto upload = new AssetUpload(filename); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { return nullptr; } - - auto upload = new AssetUpload(this, filename); +} - upload->moveToThread(thread()); - - return upload; +AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& extension) { + if (haveAssetServer()) { + auto upload = new AssetUpload(data, extension); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { + return nullptr; + } } bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 9b82e63b58..22933ea30b 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -45,6 +45,7 @@ public: Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); + Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension); private slots: void handleAssetGetInfoReply(QSharedPointer packet, SharedNodePointer senderNode); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 92632a43e5..0518b1e7a1 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -17,59 +17,94 @@ #include "AssetClient.h" #include "NetworkLogging.h" -AssetUpload::AssetUpload(QObject* object, const QString& filename) : +const QString AssetUpload::PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + +AssetUpload::AssetUpload(const QByteArray& data, const QString& extension) : + _data(data), + _extension(extension) +{ + +} + +AssetUpload::AssetUpload(const QString& filename) : _filename(filename) { } +QString AssetUpload::getErrorString() const { + // figure out the right error message for error + switch (_error) { + case AssetUpload::PermissionDenied: + return PERMISSION_DENIED_ERROR; + case AssetUpload::TooLarge: + return "The uploaded content was too large and could not be stored in the asset-server."; + case AssetUpload::FileOpenError: + return "The file could not be opened. Please check your permissions and try again."; + case AssetUpload::NetworkError: + return "The file could not be opened. Please check your network connectivity."; + default: + // not handled, do not show a message box + return QString(); + } +} + void AssetUpload::start() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); return; } - // try to open the file at the given filename - QFile file { _filename }; + auto data = _data; - if (file.open(QIODevice::ReadOnly)) { + if (data.isEmpty() && !_filename.isEmpty()) { + // try to open the file at the given filename + QFile file { _filename }; - // file opened, read the data and grab the extension - _extension = QFileInfo(_filename).suffix(); - - auto data = file.readAll(); - - // ask the AssetClient to upload the asset and emit the proper signals from the passed callback - auto assetClient = DependencyManager::get(); - - qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - - assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ - if (!responseReceived) { - _error = NetworkError; - } else { - switch (error) { - case AssetServerError::NoError: - _error = NoError; - break; - case AssetServerError::AssetTooLarge: - _error = TooLarge; - break; - case AssetServerError::PermissionDenied: - _error = PermissionDenied; - break; - default: - _error = FileOpenError; - break; - } - } - emit finished(this, hash); - }); - } else { - // we couldn't open the file - set the error result - _error = FileOpenError; - - // emit that we are done - emit finished(this, QString()); + if (file.open(QIODevice::ReadOnly)) { + + // file opened, read the data and grab the extension + _extension = QFileInfo(_filename).suffix(); + + data = file.readAll(); + } else { + // we couldn't open the file - set the error result + _error = FileOpenError; + + // emit that we are done + emit finished(this, QString()); + } } + + // ask the AssetClient to upload the asset and emit the proper signals from the passed callback + auto assetClient = DependencyManager::get(); + + if (!_filename.isEmpty()) { + qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; + } else { + qCDebug(asset_client) << "Attempting to upload file of" << _data.size() << "bytes to asset-server."; + } + + + assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ + if (!responseReceived) { + _error = NetworkError; + } else { + switch (error) { + case AssetServerError::NoError: + _error = NoError; + break; + case AssetServerError::AssetTooLarge: + _error = TooLarge; + break; + case AssetServerError::PermissionDenied: + _error = PermissionDenied; + break; + default: + _error = FileOpenError; + break; + } + } + emit finished(this, hash); + }); } diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 80faa8a9c1..92f677cbf2 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -35,13 +35,17 @@ public: FileOpenError }; - AssetUpload(QObject* parent, const QString& filename); + static const QString PERMISSION_DENIED_ERROR; + + AssetUpload(const QString& filename); + AssetUpload(const QByteArray& data, const QString& extension); Q_INVOKABLE void start(); const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } const Error& getError() const { return _error; } + QString getErrorString() const; signals: void finished(AssetUpload* upload, const QString& hash); @@ -49,6 +53,7 @@ signals: private: QString _filename; + QByteArray _data; QString _extension; Error _error; }; From c84d2fc36a29ab8ad3fe5163fb9453329445b6f5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 13 Oct 2015 10:44:43 -0700 Subject: [PATCH 40/57] fix target script url --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 1b3dd07e5e..13e5f89914 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -498,7 +498,7 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL, + script: targetsScriptURL, userData: JSON.stringify({ resetMe: { resetMe: true diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 5eaa35372f..f855caa9a1 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -469,7 +469,7 @@ MasterReset = function() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL, + script: targetsScriptURL, userData: JSON.stringify({ resetMe: { resetMe: true From 65976f78917104432ffeaaa1ef0710b91266bb31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:51:57 -0700 Subject: [PATCH 41/57] add saving of entity file to ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 33 ++++++++++++++++++++++- interface/src/assets/ATPAssetMigrator.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index d32378085b..6dd1a04434 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -31,6 +31,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { return instance; } +static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; void ATPAssetMigrator::loadEntityServerFile() { @@ -169,9 +170,39 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { - // show a dialog to ask the user where they want to save the file + saveEntityServerFile(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } } + +void ATPAssetMigrator::saveEntityServerFile() { + // show a dialog to ask the user where they want to save the file + QString saveName = QFileDialog::getSaveFileName(_dialogParent, "Save Migrated Entities File"); + + QFile saveFile { saveName }; + + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonObject rootObject; + rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray; + + QJsonDocument newDocument { rootObject }; + QByteArray jsonDataForFile; + + if (gzip(newDocument.toJson(), jsonDataForFile, -1)) { + + saveFile.write(jsonDataForFile); + saveFile.close(); + + QMessageBox::information(_dialogParent, "Success", + QString("Your new entities file has been saved at %1").arg(saveName)); + } else { + QMessageBox::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); + } + + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not open file at %1 to write new entities file to.").arg(saveName)); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 84f05b600d..2c96b55ba2 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -33,6 +33,7 @@ private slots: void assetUploadFinished(AssetUpload* upload, const QString& hash); private: void migrateResource(ResourceRequest* request); + void saveEntityServerFile(); QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; From 93e908a592093671fbd6c1e19c97c2a58a5e54f6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 13 Oct 2015 12:15:13 -0700 Subject: [PATCH 42/57] Don't reload/recenter some avatar data on startup, when we don't necessarilly have all the required data initialized. --- interface/src/Application.cpp | 6 +-- interface/src/Application.h | 2 +- interface/src/PluginContainerProxy.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 62 ++++++++++++++------------ interface/src/avatar/MyAvatar.h | 2 +- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 30014f8906..aad480edde 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1559,7 +1559,7 @@ void Application::keyPressEvent(QKeyEvent* event) { cursor->setIcon(Cursor::Icon::DEFAULT); } } else { - resetSensors(); + resetSensors(true); } break; } @@ -3591,7 +3591,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi renderArgs->_viewport = originalViewport; } -void Application::resetSensors() { +void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -3603,7 +3603,7 @@ void Application::resetSensors() { QPoint windowCenter = mainWindow->geometry().center(); _glWidget->cursor().setPos(currentScreen, windowCenter); - getMyAvatar()->reset(); + getMyAvatar()->reset(andReload); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } diff --git a/interface/src/Application.h b/interface/src/Application.h index a2125e7e09..dc714ad82a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -274,7 +274,7 @@ public slots: void setRawAvatarUpdateThreading(); void setRawAvatarUpdateThreading(bool isThreaded); - void resetSensors(); + void resetSensors(bool andReload = false); void setActiveFaceTracker(); #ifdef HAVE_IVIEWHMD diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index 19bb6ab8de..079d6d0bea 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -144,7 +144,7 @@ void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) { void PluginContainerProxy::requestReset() { // We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway. - qApp->resetSensors(); + qApp->resetSensors(true); } void PluginContainerProxy::showDisplayPluginsTools() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..f5c3acd386 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -140,16 +140,18 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { return AvatarData::toByteArray(cullSmallChanges, sendAll); } -void MyAvatar::reset() { +void MyAvatar::reset(bool andReload) { // Gather animation mode... // This should be simpler when we have only graph animations always on. bool isRig = _rig->getEnableRig(); // seting rig animation to true, below, will clear the graph animation menu item, so grab it now. bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph); // ... and get to sane configuration where other activity won't bother us. - qApp->setRawAvatarUpdateThreading(false); - _rig->disableHands = true; - setEnableRigAnimations(true); + if (andReload) { + qApp->setRawAvatarUpdateThreading(false); + _rig->disableHands = true; + setEnableRigAnimations(true); + } // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; @@ -158,32 +160,34 @@ void MyAvatar::reset() { _targetVelocity = glm::vec3(0.0f); setThrust(glm::vec3(0.0f)); - // Get fresh data, in case we're really slow and out of wack. - _hmdSensorMatrix = qApp->getHMDSensorPose(); - _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); - _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); + if (andReload) { + // Get fresh data, in case we're really slow and out of wack. + _hmdSensorMatrix = qApp->getHMDSensorPose(); + _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); - // Reset body position/orientation under the head. - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. - auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; - glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); - glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + // Reset body position/orientation under the head. + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); + glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - // FIXME: Hack to retain the previous behavior wrt height. - // I'd like to make the body match head height, but that will have to wait for separate PR. - worldBodyPos.y = getPosition().y; + // FIXME: Hack to retain the previous behavior wrt height. + // I'd like to make the body match head height, but that will have to wait for separate PR. + worldBodyPos.y = getPosition().y; - setPosition(worldBodyPos); - setOrientation(worldBodyRot); - // If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor), - // we can make that right by setting _bodySensorMatrix = newBodySensorMatrix. - // However, doing so will make the head want to point to the previous body orientation, as cached above. - //_bodySensorMatrix = newBodySensorMatrix; - //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + setPosition(worldBodyPos); + setOrientation(worldBodyRot); + // If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor), + // we can make that right by setting _bodySensorMatrix = newBodySensorMatrix. + // However, doing so will make the head want to point to the previous body orientation, as cached above. + //_bodySensorMatrix = newBodySensorMatrix; + //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes - _skeletonModel.simulate(0.1f); // non-zero - setEnableRigAnimations(false); - _skeletonModel.simulate(0.1f); + _skeletonModel.simulate(0.1f); // non-zero + setEnableRigAnimations(false); + _skeletonModel.simulate(0.1f); + } if (isRig) { setEnableRigAnimations(true); Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true); @@ -191,8 +195,10 @@ void MyAvatar::reset() { setEnableAnimGraph(true); Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true); } - _rig->disableHands = false; - qApp->setRawAvatarUpdateThreading(); + if (andReload) { + _rig->disableHands = false; + qApp->setRawAvatarUpdateThreading(); + } } void MyAvatar::update(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..02c9f53082 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -58,7 +58,7 @@ public: AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } - void reset(); + void reset(bool andReload = false); void update(float deltaTime); void preRender(RenderArgs* renderArgs); From ad96d7692191bd959f7760cb6b740d8f19d26493 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 15:06:11 -0700 Subject: [PATCH 43/57] implement comfort mode hack --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/MyAvatar.cpp | 79 ++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae41ca2493..3d671b0447 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -461,6 +461,7 @@ Menu::Menu() { 0, false, &ConnexionClient::getInstance(), SLOT(toggleConnexion(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4c9c3ef7b5..216a410603 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -159,6 +159,7 @@ namespace MenuOption { const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; + const QString ComfortMode = "Comfort Mode"; const QString Connexion = "Activate 3D Connexion Devices"; const QString Console = "Console..."; const QString ControlWithSpeech = "Control With Speech"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..55690c98f1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1514,33 +1514,68 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; - if (targetSpeed != 0.0f) { - const float ROTATION_RAMP_TIMESCALE = 0.1f; - float blend = deltaTime / ROTATION_RAMP_TIMESCALE; - if (blend > 1.0f) { - blend = 1.0f; - } - _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; - } else if (_bodyYawDelta != 0.0f) { - // attenuate body rotation speed - const float ROTATION_DECAY_TIMESCALE = 0.05f; - float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; - if (attenuation < 0.0f) { - attenuation = 0.0f; - } - _bodyYawDelta *= attenuation; + float targetSpeed = 0.0f; + + // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping + bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); + bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode(); + + if (!isHMDMode || !isComfortMode) { + targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; + + if (targetSpeed != 0.0f) { + const float ROTATION_RAMP_TIMESCALE = 0.1f; + float blend = deltaTime / ROTATION_RAMP_TIMESCALE; + if (blend > 1.0f) { + blend = 1.0f; + } + _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; + } + else if (_bodyYawDelta != 0.0f) { + // attenuate body rotation speed + const float ROTATION_DECAY_TIMESCALE = 0.05f; + float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; + if (attenuation < 0.0f) { + attenuation = 0.0f; + } + _bodyYawDelta *= attenuation; + + float MINIMUM_ROTATION_RATE = 2.0f; + if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { + _bodyYawDelta = 0.0f; + } + } + + // update body orientation by movement inputs + setOrientation(getOrientation() * + glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); + + } else { + // comfort mode.... + _bodyYawDelta = 0.0f; + + static quint64 lastPulse = 0; + quint64 now = usecTimestampNow(); + quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per second + + float driveLeft = _driveKeys[ROT_LEFT]; + float driveRight= _driveKeys[ROT_RIGHT]; + + if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) { + lastPulse = now; + + const float SNAP_TURN_DELTA = 15.0f; // degrees + float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f; + float turnAmount = direction * SNAP_TURN_DELTA; + + // update body orientation by movement inputs + setOrientation(getOrientation() * + glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f)))); - float MINIMUM_ROTATION_RATE = 2.0f; - if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { - _bodyYawDelta = 0.0f; } } getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); - // update body orientation by movement inputs - setOrientation(getOrientation() * - glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); if (qApp->getAvatarUpdater()->isHMDMode()) { glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); From c468cabe7e75cc7322f2fff42c83686e3d18d5cb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 13 Oct 2015 15:12:01 -0700 Subject: [PATCH 44/57] Fix crash when atp url is empty --- libraries/networking/src/AssetResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index ecbe80cddb..eba9e45e5c 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -24,7 +24,7 @@ void AssetResourceRequest::doSend() { // Make request to atp auto assetClient = DependencyManager::get(); auto parts = _url.path().split(".", QString::SkipEmptyParts); - auto hash = parts[0]; + auto hash = parts.length() > 0 ? parts[0] : ""; auto extension = parts.length() > 1 ? parts[1] : ""; if (hash.length() != SHA256_HASH_HEX_LENGTH) { From 549043f25533ce5506c70b02db11671ac76cd843 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 15:20:37 -0700 Subject: [PATCH 45/57] add an option to bulk upload assets in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 141 +++++++++++++++------- interface/src/assets/ATPAssetMigrator.h | 6 + 2 files changed, 105 insertions(+), 42 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 6dd1a04434..a4325d482f 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -33,6 +33,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; +static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration"; void ATPAssetMigrator::loadEntityServerFile() { auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", @@ -41,6 +42,19 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!filename.isEmpty()) { qDebug() << "Selected filename for ATP asset migration: " << filename; + static const QString MIGRATION_CONFIRMATION_TEXT { + "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ + " current asset-server and then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ + " continue?\n\nMake sure you are connected to the right domain." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::No) { + return; + } + // try to open the file at the given filename QFile modelsFile { filename }; @@ -62,50 +76,42 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!modelURLString.isEmpty()) { QUrl modelURL = QUrl(modelURLString); - if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS - || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + if (!_ignoredUrls.contains(modelURL) + && (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP)) { - QMessageBox messageBox; - messageBox.setWindowTitle("Asset Migration"); - messageBox.setText("Would you like to migrate the following file to the asset server?"); - messageBox.setInformativeText(modelURL.toDisplayString()); - messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - messageBox.setDefaultButton(QMessageBox::Ok); - - if (messageBox.exec() == QMessageBox::Ok) { - // user wants to migrate this asset + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else if (wantsToMigrateResource(modelURL)) { + auto request = ResourceManager::createResourceRequest(this, modelURL); - if (_pendingReplacements.contains(modelURL)) { - // we already have a request out for this asset, just store the QJsonValueRef - // so we can do the hash replacement when the request comes back - _pendingReplacements.insert(modelURL, jsonValue); - } else if (_uploadedAssets.contains(modelURL)) { - // we already have a hash for this asset - // so just do the replacement immediately - entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); - jsonValue = entityObject; - } else { - auto request = ResourceManager::createResourceRequest(this, modelURL); - - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - request->send(); - } - + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + + request->send(); + } else { + _ignoredUrls.insert(modelURL); } } } @@ -165,16 +171,58 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h value = valueObject; } + // add this URL to our list of uploaded assets + _uploadedAssets.insert(modelURL, atpURL); + // pull the replaced models from _pendingReplacements _pendingReplacements.remove(modelURL); // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { saveEntityServerFile(); + + // reset after the attempted save, success or fail + reset(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } + + upload->deleteLater(); +} + +bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { + static bool hasAskedForCompleteMigration { false }; + static bool wantsCompleteMigration { false }; + + + + if (!hasAskedForCompleteMigration) { + // this is the first resource migration - ask the user if they just want to migrate everything + static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ + "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n\n"\ + "Select \"No\" to be prompted for each discovered asset." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::Yes) { + wantsCompleteMigration = true; + } + + hasAskedForCompleteMigration = true; + } + + if (wantsCompleteMigration) { + return true; + } else { + // present a dialog asking the user if they want to migrate this specific resource + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + url.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + return button == QMessageBox::Yes; + } } void ATPAssetMigrator::saveEntityServerFile() { @@ -206,3 +254,12 @@ void ATPAssetMigrator::saveEntityServerFile() { QString("Could not open file at %1 to write new entities file to.").arg(saveName)); } } + +void ATPAssetMigrator::reset() { + _entitiesArray = QJsonArray(); + _doneReading = false; + _pendingReplacements.clear(); + _uploadedAssets.clear(); + _originalURLs.clear(); + _ignoredUrls.clear(); +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 2c96b55ba2..454eb1eac1 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -17,6 +17,7 @@ #include #include #include +#include class AssetUpload; class ResourceRequest; @@ -35,6 +36,10 @@ private: void migrateResource(ResourceRequest* request); void saveEntityServerFile(); + void reset(); + + bool wantsToMigrateResource(const QUrl& url); + QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; @@ -43,6 +48,7 @@ private: QMultiHash _pendingReplacements; QHash _uploadedAssets; QHash _originalURLs; + QSet _ignoredUrls; }; From 14503053902da26c91daa4e47206ee4cc2495f95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:13:47 -0700 Subject: [PATCH 46/57] fix ATP url scheme constant --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index a4325d482f..e5c31d917c 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -162,7 +162,7 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash From 8a2e23cae4909057bb1bf2c7c9505b704dcaebe6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 16:20:52 -0700 Subject: [PATCH 47/57] CR feedback --- interface/src/avatar/MyAvatar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 55690c98f1..aa579b670f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1530,8 +1530,7 @@ void MyAvatar::updateOrientation(float deltaTime) { blend = 1.0f; } _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; - } - else if (_bodyYawDelta != 0.0f) { + } else if (_bodyYawDelta != 0.0f) { // attenuate body rotation speed const float ROTATION_DECAY_TIMESCALE = 0.05f; float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; @@ -1551,12 +1550,14 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); } else { - // comfort mode.... + // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll + // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another + // snap turn every half second. _bodyYawDelta = 0.0f; static quint64 lastPulse = 0; quint64 now = usecTimestampNow(); - quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per second + quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second float driveLeft = _driveKeys[ROT_LEFT]; float driveRight= _driveKeys[ROT_RIGHT]; From 76bfc6218a8e685acb5a09473aefafeb06ffda91 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:36:06 -0700 Subject: [PATCH 48/57] add back block removed in merge --- libraries/networking/src/AssetUpload.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index fdf1c0efc4..67c1049d1a 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -100,6 +100,11 @@ void AssetUpload::start() { break; } } + + if (_error == NoError && hash == hashData(_data).toHex()) { + saveToCache(getUrl(hash, _extension), _data); + } + emit finished(this, hash); }); } From cc9376707744c28511edb506902c9c3bfe3654a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:38:00 -0700 Subject: [PATCH 49/57] remove some extra spaces --- interface/src/assets/ATPAssetMigrator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index e5c31d917c..57c0df16ff 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -195,8 +195,6 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { static bool hasAskedForCompleteMigration { false }; static bool wantsCompleteMigration { false }; - - if (!hasAskedForCompleteMigration) { // this is the first resource migration - ask the user if they just want to migrate everything static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ From d22c60204137b7e62bfdeb87cb42c30a74fd7616 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:44:13 -0700 Subject: [PATCH 50/57] use categorized logging in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 57c0df16ff..56adc38f9b 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ #include "../ui/AssetUploadDialogFactory.h" +Q_DECLARE_LOGGING_CATEGORY(asset_migrator); +Q_LOGGING_CATEGORY(asset_migrator, "hf.asset_migrator"); + ATPAssetMigrator& ATPAssetMigrator::getInstance() { static ATPAssetMigrator instance; return instance; @@ -40,7 +44,7 @@ void ATPAssetMigrator::loadEntityServerFile() { QString(), QString("Entity-Server Content (*.gz)")); if (!filename.isEmpty()) { - qDebug() << "Selected filename for ATP asset migration: " << filename; + qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; static const QString MIGRATION_CONFIRMATION_TEXT { "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ @@ -92,7 +96,7 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL // to an ATP one once ready @@ -138,7 +142,7 @@ void ATPAssetMigrator::migrateResource(ResourceRequest* request) { // add this URL to our hash of AssetUpload to original URL _originalURLs.insert(upload, request->getUrl()); - qDebug() << "Starting upload of asset from" << request->getUrl(); + qCDebug(asset_migrator) << "Starting upload of asset from" << request->getUrl(); // connect to the finished signal so we know when the AssetUpload is done QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); From 574089530a8c8ba040b708c98b6d1d6614cdedbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:49:12 -0700 Subject: [PATCH 51/57] make sure request is not nullptr before using it --- interface/src/assets/ATPAssetMigrator.cpp | 39 +++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 56adc38f9b..c268b351fa 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -96,24 +96,29 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + if (request) { + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retrieve asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + request->send(); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not create request for asset at %1").arg(modelURL.toString())); + } - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - - request->send(); } else { _ignoredUrls.insert(modelURL); } From 2caa7f6d647ab3c75d078caab670388e47cf755b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:56:20 -0700 Subject: [PATCH 52/57] change getUrl to getATPUrl, use in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 3 ++- libraries/networking/src/AssetRequest.cpp | 5 ----- libraries/networking/src/AssetRequest.h | 2 +- libraries/networking/src/AssetUpload.cpp | 2 +- libraries/networking/src/AssetUtils.cpp | 4 ++-- libraries/networking/src/AssetUtils.h | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index c268b351fa..861a1df772 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -171,7 +171,8 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); + + QString atpURL = getATPUrl(hash, upload->getExtension()).toString(); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 121b4cd4fd..7e647e9142 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -127,8 +127,3 @@ void AssetRequest::start() { }); }); } - -QUrl AssetRequest::getUrl() const { - return ::getUrl(_hash, _extension); -} - diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index a5275e718a..3c3459b15d 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -46,7 +46,7 @@ public: const QByteArray& getData() const { return _data; } const State& getState() const { return _state; } const Error& getError() const { return _error; } - QUrl getUrl() const; + QUrl getUrl() const { return ::getATPUrl(_hash, _extension); } signals: void finished(AssetRequest* thisRequest); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 67c1049d1a..e6f467e717 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -102,7 +102,7 @@ void AssetUpload::start() { } if (_error == NoError && hash == hashData(_data).toHex()) { - saveToCache(getUrl(hash, _extension), _data); + saveToCache(getATPUrl(hash, _extension), _data); } emit finished(this, hash); diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 7311d73525..f37e0af820 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -19,7 +19,7 @@ #include "ResourceManager.h" -QUrl getUrl(const QString& hash, const QString& extension) { +QUrl getATPUrl(const QString& hash, const QString& extension) { if (!extension.isEmpty()) { return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension)); } else { @@ -66,4 +66,4 @@ bool saveToCache(const QUrl& url, const QByteArray& file) { qCWarning(asset_client) << "No disk cache to save assets to."; } return false; -} \ No newline at end of file +} diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 5fd5c9144d..21b6b3f434 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -32,7 +32,7 @@ enum AssetServerError : uint8_t { PermissionDenied }; -QUrl getUrl(const QString& hash, const QString& extension = QString()); +QUrl getATPUrl(const QString& hash, const QString& extension = QString()); QByteArray hashData(const QByteArray& data); From d84994d8e0a1da6bdef40cdb13951e3281882116 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 17:04:45 -0700 Subject: [PATCH 53/57] fix casing for ubuntu include failure --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 861a1df772..fadf4ca7ad 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include From e484a904a2da25dba7506411c0759480a2c4134f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 13 Oct 2015 17:36:00 -0700 Subject: [PATCH 54/57] Rotate the avatar to align with the HMD while moving MyAvatar: refactored updateFromHMDSensorMatrix() a bit by splitting it into several methods, because it was getting quite large and becoming hard to follow. * beginStraighteningLean() - can be called when we would like to trigger a re-centering action. * shouldBeginStraighteningLean() - contains some of the logic to decide if we should begin a re-centering action. for now it encapulates the capsule check. * processStraighteningLean() - performs the actual re-centering calculation. New code was added to MyAvatar::updateFromHMDSensorMatrix() to trigger re-centering when the avatar speed rises over a threshold. Secondly the Rig::computeMotionAnimationState() state machine for animGraph added a state change hysteresis of 100ms. This hysteresis should help smooth over two issues. 1) When the delta position is 0, because the physics timestep was not evaluated. 2) During re-centering due to desired motion, the avatar velocity can fluctuate causing undesired animation state fluctuation. --- interface/src/avatar/MyAvatar.cpp | 53 +++++++- interface/src/avatar/MyAvatar.h | 6 + libraries/animation/src/AnimStateMachine.cpp | 2 +- libraries/animation/src/Rig.cpp | 133 +++++++++++++------ libraries/animation/src/Rig.h | 2 + 5 files changed, 145 insertions(+), 51 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..bb16ac6e21 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -320,6 +320,7 @@ static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRa // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { + // calc deltaTime auto now = usecTimestampNow(); auto deltaUsecs = now - _lastUpdateFromHMDTime; _lastUpdateFromHMDTime = now; @@ -334,21 +335,61 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); - const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds + // It can be more accurate/smooth to use velocity rather than position, + // but some modes (e.g., hmd standing) update position without updating velocity. + // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 positionDelta = getPosition() - _lastPosition; + glm::vec3 workingVelocity = positionDelta / deltaTime; + _lastPosition = getPosition(); + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + bool isMoving; + if (_lastIsMoving) { + isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + + if (shouldBeginStraighteningLean() || hmdIsAtRest || justStartedMoving) { + beginStraighteningLean(); + } + + processStraighteningLean(deltaTime); +} + +void MyAvatar::beginStraighteningLean() { + // begin homing toward derived body position. + if (!_straighteningLean) { + _straighteningLean = true; + _straighteningLeanAlpha = 0.0f; + } +} + +bool MyAvatar::shouldBeginStraighteningLean() const { // define a vertical capsule const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. + // detect if the derived body position is outside of a capsule around the _bodySensorMatrix auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { + bool isBodyPosOutsideCapsule = capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS); - // begin homing toward derived body position. - _straighteningLean = true; - _straighteningLeanAlpha = 0.0f; + if (isBodyPosOutsideCapsule) { + return true; + } else { + return false; + } +} - } else if (_straighteningLean) { +void MyAvatar::processStraighteningLean(float deltaTime) { + if (_straighteningLean) { + + const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..3b913eb509 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -271,6 +271,10 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } + void beginStraighteningLean(); + bool shouldBeginStraighteningLean() const; + void processStraighteningLean(float deltaTime); + bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead @@ -366,6 +370,8 @@ private: quint64 _lastUpdateFromHMDTime = usecTimestampNow(); AtRestDetector _hmdAtRestDetector; + glm::vec3 _lastPosition; + bool _lastIsMoving = false; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 5304cefe46..c0bb66c2a0 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -93,7 +93,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe const float dt = 0.0f; Triggers triggers; _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); -#if WANT_DEBUGa +#if WANT_DEBUG qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget; #endif _currentState = desiredState; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 340c09060a..25f28a3f64 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -437,16 +437,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos static float t = 0.0f; _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); - // default anim vars to notMoving and notTurning - _animVars.set("isMovingForward", false); - _animVars.set("isMovingBackward", false); - _animVars.set("isMovingLeft", false); - _animVars.set("isMovingRight", false); - _animVars.set("isNotMoving", true); - _animVars.set("isTurningLeft", false); - _animVars.set("isTurningRight", false); - _animVars.set("isNotTurning", true); - const float ANIM_WALK_SPEED = 1.4f; // m/s _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); @@ -470,47 +460,102 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } if (glm::length(localVel) > moveThresh) { - if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { - if (forwardSpeed > 0.0f) { - // forward - _animVars.set("isMovingForward", true); - _animVars.set("isNotMoving", false); - - } else { - // backward - _animVars.set("isMovingBackward", true); - _animVars.set("isNotMoving", false); - } - } else { - if (lateralSpeed > 0.0f) { - // right - _animVars.set("isMovingRight", true); - _animVars.set("isNotMoving", false); - } else { - // left - _animVars.set("isMovingLeft", true); - _animVars.set("isNotMoving", false); - } + if (_desiredState != RigRole::Move) { + _desiredStateAge = 0.0f; } - _state = RigRole::Move; + _desiredState = RigRole::Move; } else { if (fabsf(turningSpeed) > turnThresh) { - if (turningSpeed > 0.0f) { - // turning right - _animVars.set("isTurningRight", true); - _animVars.set("isNotTurning", false); - } else { - // turning left - _animVars.set("isTurningLeft", true); - _animVars.set("isNotTurning", false); + if (_desiredState != RigRole::Turn) { + _desiredStateAge = 0.0f; } - _state = RigRole::Turn; - } else { - // idle - _state = RigRole::Idle; + _desiredState = RigRole::Turn; + } else { // idle + if (_desiredState != RigRole::Idle) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Idle; } } + const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; + + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { + _state = _desiredState; + _desiredStateAge = 0.0f; + } + + _desiredStateAge += deltaTime; + + if (_state == RigRole::Move) { + if (glm::length(localVel) > MOVE_ENTER_SPEED_THRESHOLD) { + if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { + if (forwardSpeed > 0.0f) { + // forward + _animVars.set("isMovingForward", true); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + + } else { + // backward + _animVars.set("isMovingBackward", true); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + } + } else { + if (lateralSpeed > 0.0f) { + // right + _animVars.set("isMovingRight", true); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } else { + // left + _animVars.set("isMovingLeft", true); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } + } + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + } else if (_state == RigRole::Turn) { + if (turningSpeed > 0.0f) { + // turning right + _animVars.set("isTurningRight", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isNotTurning", false); + } else { + // turning left + _animVars.set("isTurningLeft", true); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", false); + } + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", true); + } else { + // default anim vars to notMoving and notTurning + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + t += deltaTime; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d9f7b4688..71c27e7213 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -236,6 +236,8 @@ public: Move }; RigRole _state = RigRole::Idle; + RigRole _desiredState = RigRole::Idle; + float _desiredStateAge = 0.0f; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; }; From c4af4c7b101735e1f9e78b9e8a4c004ba5f5607a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 17:45:29 -0700 Subject: [PATCH 55/57] basic mono preview for the oculus display plugin --- .../oculus/OculusDisplayPlugin.cpp | 30 +++++++++++++++---- .../oculus/OculusDisplayPlugin.h | 4 ++- .../stereo/StereoDisplayPlugin.cpp | 5 ++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 58675eab4d..3e2290f104 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -11,6 +11,8 @@ #include "OculusHelpers.h" +#include + #if (OVR_MAJOR_VERSION >= 6) // A base class for FBO wrappers that need to use the Oculus C @@ -135,6 +137,19 @@ const QString & OculusDisplayPlugin::getName() const { return NAME; } +static const QString MONO_PREVIEW = "Mono Preview"; +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + +void OculusDisplayPlugin::activate() { + + CONTAINER->addMenuItem(MENU_PATH(), MONO_PREVIEW, + [this](bool clicked) { + _monoPreview = clicked; + }, true, true); + CONTAINER->removeMenu(FRAMERATE); + OculusBaseDisplayPlugin::activate(); +} + void OculusDisplayPlugin::customizeContext() { WindowOpenGLDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) @@ -149,7 +164,7 @@ void OculusDisplayPlugin::customizeContext() { #endif enableVsync(false); // Only enable mirroring if we know vsync is disabled - _enableMirror = !isVsyncEnabled(); + _enablePreview = !isVsyncEnabled(); } void OculusDisplayPlugin::deactivate() { @@ -169,10 +184,15 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi // controlling vsync wglSwapIntervalEXT(0); - // screen mirroring - if (_enableMirror) { + // screen preview mirroring + if (_enablePreview) { auto windowSize = toGlm(_window->size()); - Context::Viewport(windowSize.x, windowSize.y); + if (_monoPreview) { + Context::Viewport(windowSize.x * 2, windowSize.y); + Context::Scissor(0, windowSize.y, windowSize.x, windowSize.y); + } else { + Context::Viewport(windowSize.x, windowSize.y); + } glBindTexture(GL_TEXTURE_2D, finalTexture); GLenum err = glGetError(); Q_ASSERT(0 == err); @@ -216,7 +236,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi otherwise the swapbuffer delay will interefere with the framerate of the headset */ void OculusDisplayPlugin::finishFrame() { - if (_enableMirror) { + if (_enablePreview) { swapBuffers(); } doneCurrent(); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h index 7db83884cd..c1224ecf3a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -14,6 +14,7 @@ using SwapFboPtr = QSharedPointer; class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: + virtual void activate() override; virtual void deactivate() override; virtual const QString & getName() const override; @@ -25,7 +26,8 @@ protected: private: static const QString NAME; - bool _enableMirror{ false }; + bool _enablePreview { false }; + bool _monoPreview { true }; #if (OVR_MAJOR_VERSION >= 6) SwapFboPtr _sceneFbo; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 2ea79ed2e0..4f7b0a1a78 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -61,6 +61,8 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje return eyeProjection; } +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + std::vector _screenActions; void StereoDisplayPlugin::activate() { auto screens = qApp->screens(); @@ -76,6 +78,9 @@ void StereoDisplayPlugin::activate() { [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } + + CONTAINER->removeMenu(FRAMERATE); + CONTAINER->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); } From 3e7a6fd490cfbd1bf327d71d4d6a6b6d30389963 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 14 Oct 2015 14:43:59 -0700 Subject: [PATCH 56/57] Change to boom offset for 3rd person HMD camera. Previously the boom offset was computed in HMD sensor space instead of world space, so it did not play well with the vive, or the oculus if you weren't facing the camera directly. Now it is computed in world space, and is always behind the character's position/orientation. This can cause the boom to swing if the character rotation changes but now the avatar rotation in HMD uses comfort mode. It's not that disorienting. --- interface/src/Application.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0e5003830..c9a07c1d13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1109,13 +1109,10 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * hmdRotation); - // Ignore MenuOption::CenterPlayerInView in HMD view - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); + auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); } else { _myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { From c99573ea64a812c417a1dc2a7a3d3f786eed653b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 15 Oct 2015 15:35:35 +0200 Subject: [PATCH 57/57] Fixed support for calling setJointMumblers with joint name. Walk.js runs smooth now! --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- libraries/avatars/src/AvatarData.cpp | 21 +++++++++++++++++++-- libraries/avatars/src/AvatarData.h | 2 ++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 5b42a8fb78..d1192deee7 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 02bc23a2e2..0b5bcab65a 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a9ff9541ea..a698c6f374 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1032,13 +1032,30 @@ glm::vec3 AvatarData::getJointTranslation(const QString& name) const { void AvatarData::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), - Q_ARG(const glm::quat&, rotation)); + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); return; } setJointData(getJointIndex(name), rotation, translation); } +void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation)); + return; + } + setJointRotation(getJointIndex(name), rotation); +} + +void AvatarData::setJointTranslation(const QString& name, const glm::vec3& translation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(const QString&, name), + Q_ARG(const glm::vec3&, translation)); + return; + } + setJointTranslation(getJointIndex(name), translation); +} + void AvatarData::setJointRotation(int index, const glm::quat& rotation) { if (index == -1) { return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e4022fd474..3abd63bf63 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -251,6 +251,8 @@ public: Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE void setJointRotation(const QString& name, const glm::quat& rotation); + Q_INVOKABLE void setJointTranslation(const QString& name, const glm::vec3& translation); Q_INVOKABLE void clearJointData(const QString& name); Q_INVOKABLE bool isJointDataValid(const QString& name) const; Q_INVOKABLE glm::quat getJointRotation(const QString& name) const;