From 0ebaba7cf890aa7544365cff5057c87a87642e33 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 10 Apr 2017 13:36:30 -0700 Subject: [PATCH] Now supports sensorConfig with hips and chest sensors --- .../resources/avatar/avatar-animation.json | 8 +- libraries/animation/src/Rig.cpp | 3 + scripts/developer/tests/viveMotionCapture.js | 123 +++++++++++++----- scripts/developer/tests/viveTrackedObjects.js | 4 +- 4 files changed, 100 insertions(+), 38 deletions(-) diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index daa69b95f7..9efe3dd29b 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -81,10 +81,10 @@ "typeVar": "leftFootType" }, { - "jointName": "Neck", - "positionVar": "neckPosition", - "rotationVar": "neckRotation", - "typeVar": "neckType" + "jointName": "Spine2", + "positionVar": "spine2Position", + "rotationVar": "spine2Rotation", + "typeVar": "spine2Type" }, { "jointName": "Head", diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index adc40d6e81..c8717fd11e 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1033,6 +1033,9 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { } else { _animVars.set("hipsType", (int)IKTarget::Type::Unknown); } + + // by default this IK target is disabled. + _animVars.set("spine2Type", (int)IKTarget::Type::Unknown); } void Rig::updateFromEyeParameters(const EyeParameters& params) { diff --git a/scripts/developer/tests/viveMotionCapture.js b/scripts/developer/tests/viveMotionCapture.js index 0e6119a714..27c6809f5c 100644 --- a/scripts/developer/tests/viveMotionCapture.js +++ b/scripts/developer/tests/viveMotionCapture.js @@ -27,18 +27,59 @@ Controller.enableMapping(MAPPING_NAME); var leftFoot; var rightFoot; var hips; +var spine2; + +var FEET_ONLY = 0; +var FEET_AND_HIPS = 1; +var FEET_AND_CHEST = 2; +var FEET_HIPS_AND_CHEST = 3; + +var SENSOR_CONFIG_NAMES = [ + "FeetOnly", + "FeetAndHips", + "FeetAndChest", + "FeetHipsAndChest" +]; + +var ANIM_VARS = [ + "leftFootType", + "leftFootPosition", + "leftFootRotation", + "rightFootType", + "rightFootPosition", + "rightFootRotation", + "hipsType", + "hipsPosition", + "hipsRotation", + "spine2Type", + "spine2Position", + "spine2Rotation" +]; + +var sensorConfig = FEET_HIPS_AND_CHEST; var Y_180 = {x: 0, y: 1, z: 0, w: 0}; +var Y_180_XFORM = new Xform(Y_180, {x: 0, y: 0, z: 0}); function computeOffsetXform(pose, jointIndex) { var poseXform = new Xform(pose.rotation, pose.translation); + + // TODO: we can do better here... + // one idea, hang default pose skeleton from HMD head. var referenceXform = new Xform(MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(jointIndex), MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(jointIndex)); + return Xform.mul(poseXform.inv(), referenceXform); } function calibrate() { - print("AJT: calibrating"); + print("AJT: calibrating..."); + + leftFoot = undefined; + rightFoot = undefined; + hips = undefined; + spine2 = undefined; + var poses = []; if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { @@ -76,12 +117,30 @@ function calibrate() { print("AJT: rightFoot = " + JSON.stringify(rightFoot)); print("AJT: leftFoot = " + JSON.stringify(leftFoot)); - if (poses.length >= 3) { + if (sensorConfig === FEET_ONLY) { + // we're done! + } else if (sensorConfig === FEET_AND_HIPS && poses.length >= 3) { hips = poses[2]; - hips.offsetXform = computeOffsetXform(hips.pose, MyAvatar.getJointIndex("Hips")); + } else if (sensorConfig === FEET_AND_CHEST && poses.length >= 3) { + spine2 = poses[2]; + } else if (sensorConfig === FEET_HIPS_AND_CHEST && poses.length >= 4) { + hips = poses[2]; + spine2 = poses[3]; + } else { + // TODO: better error messages + print("AJT: could not calibrate for sensor config " + SENSOR_CONFIG_NAMES[sensorConfig] + ", please try again!"); + } + if (hips) { + hips.offsetXform = computeOffsetXform(hips.pose, MyAvatar.getJointIndex("Hips")); print("AJT: hips = " + JSON.stringify(hips)); } + + if (spine2) { + spine2.offsetXform = computeOffsetXform(spine2.pose, MyAvatar.getJointIndex("Spine2")); + print("AJT: spine2 = " + JSON.stringify(spine2)); + } + } else { print("AJT: could not find two trackers, try again!"); } @@ -97,6 +156,12 @@ var ikTypes = { var handlerId; +function computeIKTargetXform(jointInfo) { + var pose = Controller.getPoseValue(jointInfo.channel); + var offsetXform = jointInfo.offsetXform; + return Xform.mul(Y_180_XFORM, Xform.mul(new Xform(pose.rotation, pose.translation), offsetXform)); +} + function update(dt) { if (rightTriggerPressed && leftTriggerPressed) { if (!calibrated) { @@ -107,59 +172,53 @@ function update(dt) { MyAvatar.removeAnimationStateHandler(handlerId); } - // hook up anim state callback - var propList = [ - "leftFootType", "leftFootPosition", "leftFootRotation", - "rightFootType", "rightFootPosition", "rightFootRotation", - "hipsType", "hipsPosition", "hipsRotation" - ]; - handlerId = MyAvatar.addAnimationStateHandler(function (props) { - var result = {}, pose, offsetXform, xform; + var result = {}, xform; if (rightFoot) { - pose = Controller.getPoseValue(rightFoot.channel); - offsetXform = rightFoot.offsetXform; - - xform = Xform.mul(new Xform(pose.rotation, pose.translation), offsetXform); + xform = computeIKTargetXform(rightFoot); result.rightFootType = ikTypes.RotationAndPosition; - result.rightFootPosition = Vec3.multiplyQbyV(Y_180, xform.pos); - result.rightFootRotation = Quat.multiply(Y_180, xform.rot); - + result.rightFootPosition = xform.pos; + result.rightFootRotation = xform.rot; } else { result.rightFootType = props.rightFootType; - result.rightFootPositon = props.rightFootPosition; + result.rightFootPosition = props.rightFootPosition; result.rightFootRotation = props.rightFootRotation; } if (leftFoot) { - pose = Controller.getPoseValue(leftFoot.channel); - offsetXform = leftFoot.offsetXform; - xform = Xform.mul(new Xform(pose.rotation, pose.translation), offsetXform); + xform = computeIKTargetXform(leftFoot); result.leftFootType = ikTypes.RotationAndPosition; - result.leftFootPosition = Vec3.multiplyQbyV(Y_180, xform.pos); - result.leftFootRotation = Quat.multiply(Y_180, xform.rot); + result.leftFootPosition = xform.pos; + result.leftFootRotation = xform.rot; } else { result.leftFootType = props.leftFootType; - result.leftFootPositon = props.leftFootPosition; + result.leftFootPosition = props.leftFootPosition; result.leftFootRotation = props.leftFootRotation; } if (hips) { - pose = Controller.getPoseValue(hips.channel); - offsetXform = hips.offsetXform; - xform = Xform.mul(new Xform(pose.rotation, pose.translation), offsetXform); + xform = computeIKTargetXform(hips); result.hipsType = ikTypes.RotationAndPosition; - result.hipsPosition = Vec3.multiplyQbyV(Y_180, xform.pos); - result.hipsRotation = Quat.multiply(Y_180, xform.rot); + result.hipsPosition = xform.pos; + result.hipsRotation = xform.rot; } else { result.hipsType = props.hipsType; - result.hipsPositon = props.hipsPosition; + result.hipsPosition = props.hipsPosition; result.hipsRotation = props.hipsRotation; } + if (spine2) { + xform = computeIKTargetXform(spine2); + result.spine2Type = ikTypes.RotationAndPosition; + result.spine2Position = xform.pos; + result.spine2Rotation = xform.rot; + } else { + result.spine2Type = ikTypes.Off; + } + return result; - }, propList); + }, ANIM_VARS); } } else { diff --git a/scripts/developer/tests/viveTrackedObjects.js b/scripts/developer/tests/viveTrackedObjects.js index 78911538e4..4155afb82b 100644 --- a/scripts/developer/tests/viveTrackedObjects.js +++ b/scripts/developer/tests/viveTrackedObjects.js @@ -18,14 +18,14 @@ function shutdown() { }); } -var WHITE = {x: 1, y: 1, z: 1, w: 1}; +var BLUE = {x: 0, y: 0, z: 1, w: 1}; function update(dt) { if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); if (pose.valid) { - DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, WHITE); + DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); } else { DebugDraw.removeMyAvatarMarker(key); }