mirror of
https://github.com/lubosz/overte.git
synced 2025-08-08 03:27:48 +02:00
fixed roll rotation added to azimuth in MySkeletonModel.cpp
This commit is contained in:
parent
89b508251e
commit
3c792a04c1
4 changed files with 38 additions and 417 deletions
|
@ -447,12 +447,12 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
|
||||||
void MyAvatar::update(float deltaTime) {
|
void MyAvatar::update(float deltaTime) {
|
||||||
// update moving average of HMD facing in xz plane.
|
// update moving average of HMD facing in xz plane.
|
||||||
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
|
const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength();
|
||||||
const float PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH = 0.0f; // 100 percent shoulders
|
const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders
|
||||||
|
|
||||||
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
||||||
|
|
||||||
// put the spine facing in sensor space.
|
// put the average hand azimuth into sensor space.
|
||||||
// then mix it with head facing to determine rotation recenter
|
// then mix it with head facing direction to determine rotation recenter
|
||||||
if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) {
|
if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) {
|
||||||
glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y));
|
glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y));
|
||||||
glm::mat4 sensorToWorldMat = getSensorToWorldMatrix();
|
glm::mat4 sensorToWorldMat = getSensorToWorldMatrix();
|
||||||
|
@ -462,7 +462,7 @@ void MyAvatar::update(float deltaTime) {
|
||||||
if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) {
|
if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) {
|
||||||
normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z));
|
normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z));
|
||||||
}
|
}
|
||||||
glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH);
|
glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH);
|
||||||
_headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau);
|
_headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau);
|
||||||
} else {
|
} else {
|
||||||
_headControllerFacingMovingAverage = _headControllerFacing;
|
_headControllerFacingMovingAverage = _headControllerFacing;
|
||||||
|
@ -497,7 +497,7 @@ void MyAvatar::update(float deltaTime) {
|
||||||
|
|
||||||
if (_goToPending) {
|
if (_goToPending) {
|
||||||
setWorldPosition(_goToPosition);
|
setWorldPosition(_goToPosition);
|
||||||
setWorldOrientation(_goToOrientation);
|
setWorldOrientation(_goToOrientation);
|
||||||
_headControllerFacingMovingAverage = _headControllerFacing;
|
_headControllerFacingMovingAverage = _headControllerFacing;
|
||||||
_goToPending = false;
|
_goToPending = false;
|
||||||
// updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes
|
// updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes
|
||||||
|
@ -834,15 +834,14 @@ void MyAvatar::computeHandAzimuth() {
|
||||||
if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) {
|
if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) {
|
||||||
_hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY);
|
_hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY);
|
||||||
} else {
|
} else {
|
||||||
_hipToHandController = glm::vec2(0.0f, 1.0f);
|
_hipToHandController = glm::vec2(0.0f, -1.0f);
|
||||||
}
|
}
|
||||||
// check the angular distance from forward and back
|
// check the angular distance from forward and back
|
||||||
float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading);
|
float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading);
|
||||||
float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading);
|
float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading);
|
||||||
// if we are now closer to the 180 flip of the previous chest forward
|
// if we are now closer to the 180 flip of the previous chest forward
|
||||||
// then we negate our computed _hipToHandController to keep the chest in the same direction.
|
// then we negate our computed _hipToHandController to keep the chest from flipping.
|
||||||
if (cosBackwardAngle > cosForwardAngle) {
|
if (cosBackwardAngle > cosForwardAngle) {
|
||||||
// this means we have lost continuity with the current chest position
|
|
||||||
_hipToHandController = -_hipToHandController;
|
_hipToHandController = -_hipToHandController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3375,12 +3374,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||||
|
|
||||||
glm::mat4 MyAvatar::getSpine2RotationRigSpace() const {
|
glm::mat4 MyAvatar::getSpine2RotationRigSpace() const {
|
||||||
|
|
||||||
static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180;
|
// static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180;
|
||||||
// RIG_CHANGE_OF_BASIS * INITIAL_RIG_ROTATION * Quaternions::IDENTITY(explicit rig rotation) * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180(avatar Space);
|
// RIG_CHANGE_OF_BASIS * AVATAR_TO_RIG_ROTATION * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180; //avatar Space;
|
||||||
// INITIAL_RIG_ROTATION = Quaternions::Y_180;
|
const glm::quat AVATAR_TO_RIG_ROTATION = Quaternions::Y_180;
|
||||||
static const glm::quat INITIAL_RIG_ROTATION = Quaternions::Y_180;
|
glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y);
|
||||||
glm::quat avatarToRigSpace = INITIAL_RIG_ROTATION * Quaternions::IDENTITY;
|
|
||||||
glm::vec3 hipToHandRigSpace = avatarToRigSpace * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y);
|
|
||||||
|
|
||||||
glm::vec3 u, v, w;
|
glm::vec3 u, v, w;
|
||||||
generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w);
|
generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w);
|
||||||
|
@ -3971,7 +3968,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
||||||
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
glm::vec3 currentHeadPosition = currentHeadPose.getTranslation();
|
||||||
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition);
|
||||||
if (!isActive(Horizontal) &&
|
if (!isActive(Horizontal) &&
|
||||||
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) {
|
(glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) {
|
||||||
myAvatar.setResetMode(true);
|
myAvatar.setResetMode(true);
|
||||||
stepDetected = true;
|
stepDetected = true;
|
||||||
}
|
}
|
||||||
|
@ -3995,14 +3992,14 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
|
||||||
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
|
||||||
activate(Rotation);
|
activate(Rotation);
|
||||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||||
}
|
}
|
||||||
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
if (myAvatar.getCenterOfGravityModelEnabled()) {
|
||||||
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) {
|
||||||
activate(Horizontal);
|
activate(Horizontal);
|
||||||
if (myAvatar.getEnableStepResetRotation()) {
|
if (myAvatar.getEnableStepResetRotation()) {
|
||||||
activate(Rotation);
|
activate(Rotation);
|
||||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -4010,7 +4007,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
activate(Horizontal);
|
activate(Horizontal);
|
||||||
if (myAvatar.getEnableStepResetRotation()) {
|
if (myAvatar.getEnableStepResetRotation()) {
|
||||||
activate(Rotation);
|
activate(Rotation);
|
||||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4020,7 +4017,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
} else {
|
} else {
|
||||||
if (!isActive(Rotation) && getForceActivateRotation()) {
|
if (!isActive(Rotation) && getForceActivateRotation()) {
|
||||||
activate(Rotation);
|
activate(Rotation);
|
||||||
myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing);
|
myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing());
|
||||||
setForceActivateRotation(false);
|
setForceActivateRotation(false);
|
||||||
}
|
}
|
||||||
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
if (!isActive(Horizontal) && getForceActivateHorizontal()) {
|
||||||
|
|
|
@ -914,6 +914,8 @@ public:
|
||||||
|
|
||||||
virtual void rebuildCollisionShape() override;
|
virtual void rebuildCollisionShape() override;
|
||||||
|
|
||||||
|
const glm::vec2& getHipToHandController() const { return _hipToHandController; }
|
||||||
|
void setHipToHandController(glm::vec2 currentHipToHandController) { _hipToHandController = currentHipToHandController; }
|
||||||
const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; }
|
const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; }
|
||||||
void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; }
|
void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; }
|
||||||
const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
|
const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; }
|
||||||
|
|
|
@ -239,21 +239,28 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
||||||
|
|
||||||
// set spine2 if we have hand controllers
|
// set spine2 if we have hand controllers
|
||||||
AnimPose currentPose;
|
|
||||||
bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose);
|
|
||||||
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
|
|
||||||
glm::vec3 u, v, w;
|
|
||||||
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);
|
|
||||||
glm::vec3 up = currentPose.rot() * glm::vec3(0.0f, 1.0f, 0.0f);
|
|
||||||
generateBasisVectors(up, fwd, u, v, w);
|
|
||||||
AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)));
|
|
||||||
|
|
||||||
if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) {
|
if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) {
|
||||||
AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace());
|
AnimPose currentSpine2Pose;
|
||||||
currentPose.rot() = newSpinePose.rot();
|
AnimPose currentHeadPose;
|
||||||
|
AnimPose currentHipsPose;
|
||||||
|
bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose);
|
||||||
|
bool ret1 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose);
|
||||||
|
bool ret2 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose);
|
||||||
|
AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace());
|
||||||
|
glm::vec3 u, v, w;
|
||||||
|
glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||||
|
glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans();
|
||||||
|
if (glm::length(up) > 0.0f) {
|
||||||
|
up = glm::normalize(up);
|
||||||
|
} else {
|
||||||
|
up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
|
generateBasisVectors(up, fwd, u, v, w);
|
||||||
|
AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)));
|
||||||
|
currentSpine2Pose.rot() = newSpinePose.rot();
|
||||||
|
params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose;
|
||||||
|
params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
||||||
}
|
}
|
||||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose;
|
|
||||||
params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_prevHipsValid = false;
|
_prevHipsValid = false;
|
||||||
|
|
|
@ -1,385 +0,0 @@
|
||||||
/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat,
|
|
||||||
DebugDraw, Mat4, Entities, Xform, Controller, Camera, console, document*/
|
|
||||||
|
|
||||||
Script.registerValue("ROTATEAPP", true);
|
|
||||||
|
|
||||||
var TABLET_BUTTON_NAME = "ROTATE";
|
|
||||||
var CHANGE_OF_BASIS_ROTATION = { x: 0, y: 1, z: 0, w: 0 };
|
|
||||||
var DEFAULT_HEAD_TURN_THRESHOLD = 0.5333;
|
|
||||||
var DEFAULT_HEAD_TURN_FILTER_LENGTH = 4.0;
|
|
||||||
var LOADING_DELAY = 2000;
|
|
||||||
var AVERAGING_RATE = 0.03;
|
|
||||||
var INCREASING = 1.0;
|
|
||||||
var DECREASING = -1.0;
|
|
||||||
var DEGREES_PER_PI_RADIANS = 180.0;
|
|
||||||
var FILTER_FUDGE_RANGE = 0.9;
|
|
||||||
|
|
||||||
var activated = false;
|
|
||||||
var documentLoaded = false;
|
|
||||||
var sciptLoaded = false;
|
|
||||||
var headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var hipToLeftHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var hipToRightHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var averageAzimuth = 0.0;
|
|
||||||
var hipsPositionRigSpace = { x: 0, y: 0, z: 0 };
|
|
||||||
var spine2PositionRigSpace = { x: 0, y: 0, z: 0 };
|
|
||||||
var hipsRotationRigSpace = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var spine2RotationRigSpace = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
|
|
||||||
/*
|
|
||||||
var ikTypes = {
|
|
||||||
RotationAndPosition: 0,
|
|
||||||
RotationOnly: 1,
|
|
||||||
HmdHead: 2,
|
|
||||||
HipsRelativeRotationAndPosition: 3,
|
|
||||||
Spline: 4,
|
|
||||||
Unknown: 5
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var ANIM_VARS = [
|
|
||||||
//"headType",
|
|
||||||
"spine2Type",
|
|
||||||
//"hipsType",
|
|
||||||
"spine2Position",
|
|
||||||
"spine2Rotation"
|
|
||||||
//"hipsPosition",
|
|
||||||
//"hipsRotation"
|
|
||||||
];
|
|
||||||
|
|
||||||
var handlerId = MyAvatar.addAnimationStateHandler(function (props) {
|
|
||||||
//print("in callback");
|
|
||||||
//print("props spine2 pos: " + props.spine2Position.x + " " + props.spine2Position.y + " " + props.spine2Position.z);
|
|
||||||
//print("props hip pos: " + props.hipsPosition.x + " " + props.hipsPosition.y + " " + props.hipsPosition.z);
|
|
||||||
var result = {};
|
|
||||||
//{x:0,y:0,z:0}
|
|
||||||
//result.headType = ikTypes.HmdHead;
|
|
||||||
//result.hipsType = ikTypes.RotationAndPosition;
|
|
||||||
//result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 };
|
|
||||||
//result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; //
|
|
||||||
result.spine2Type = ikTypes.Spline;
|
|
||||||
result.spine2Position = spine2PositionRigSpace; // { x: 0, y: 1.3, z: 0 };
|
|
||||||
result.spine2Rotation = spine2Rotation;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, ANIM_VARS);
|
|
||||||
*/
|
|
||||||
// define state readings constructor
|
|
||||||
function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) {
|
|
||||||
this.headPose = headPose;
|
|
||||||
this.rhandPose = rhandPose;
|
|
||||||
this.lhandPose = lhandPose;
|
|
||||||
this.diffFromAverageEulers = diffFromAverageEulers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// define current state readings object for holding tracker readings and current differences from averages
|
|
||||||
var currentStateReadings = new StateReading(Controller.getPoseValue(Controller.Standard.Head),
|
|
||||||
Controller.getPoseValue(Controller.Standard.RightHand),
|
|
||||||
Controller.getPoseValue(Controller.Standard.LeftHand),
|
|
||||||
{ x: 0, y: 0, z: 0 });
|
|
||||||
|
|
||||||
// declare the checkbox constructor
|
|
||||||
function AppCheckbox(type,id,eventType,isChecked) {
|
|
||||||
this.type = type;
|
|
||||||
this.id = id;
|
|
||||||
this.eventType = eventType;
|
|
||||||
this.data = {value: isChecked};
|
|
||||||
}
|
|
||||||
|
|
||||||
var usingStepResetRotationDirection = new AppCheckbox("checkboxtick", "stepReset", "onStepResetCheckBox", false);
|
|
||||||
var usingDrawAverageFacing = new AppCheckbox("checkboxtick", "drawAverage", "onDrawAverageFacingCheckBox", false);
|
|
||||||
var checkBoxArray = new Array(usingStepResetRotationDirection, usingDrawAverageFacing);
|
|
||||||
|
|
||||||
// declare the html slider constructor
|
|
||||||
function AppProperty(name, type, eventType, signalType, setFunction, initValue, convertToThreshold, convertToSlider, signalOn) {
|
|
||||||
this.name = name;
|
|
||||||
this.type = type;
|
|
||||||
this.eventType = eventType;
|
|
||||||
this.signalType = signalType;
|
|
||||||
this.setValue = setFunction;
|
|
||||||
this.value = initValue;
|
|
||||||
this.get = function () {
|
|
||||||
return this.value;
|
|
||||||
};
|
|
||||||
this.convertToThreshold = convertToThreshold;
|
|
||||||
this.convertToSlider = convertToSlider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// var HTML_URL = Script.resolvePath("file:///c:/dev/hifi_fork/hifi/scripts/developer/rotateRecenterApp.html");
|
|
||||||
var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/rotateRecenterApp.html");
|
|
||||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
|
||||||
|
|
||||||
// define the sliders
|
|
||||||
var filterLengthProperty = new AppProperty("#filterLength-slider", "slider", "onFilterLengthSlider", "filterSignal",
|
|
||||||
setFilterLength, DEFAULT_HEAD_TURN_FILTER_LENGTH, function (num) {
|
|
||||||
var base = 5;
|
|
||||||
var shift = 0;
|
|
||||||
return convertExponential(base, num, INCREASING, shift);
|
|
||||||
}, function (num) {
|
|
||||||
var base = 5;
|
|
||||||
var shift = 0;
|
|
||||||
return convertLog(base, num, INCREASING, shift);
|
|
||||||
}, true);
|
|
||||||
var angleThresholdProperty = new AppProperty("#angleThreshold-slider", "slider", "onAngleThresholdSlider", "angleSignal",
|
|
||||||
setAngleThreshold, DEFAULT_HEAD_TURN_THRESHOLD, function (num) {
|
|
||||||
return convertToRadians(num);
|
|
||||||
}, function (num) {
|
|
||||||
return convertToDegrees(num);
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
var propArray = new Array(filterLengthProperty, angleThresholdProperty);
|
|
||||||
|
|
||||||
function setFilterLength(num) {
|
|
||||||
filterLengthProperty.value = num;
|
|
||||||
MyAvatar.rotationRecenterFilterLength = filterLengthProperty.value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAngleThreshold(num) {
|
|
||||||
angleThresholdProperty.value = num;
|
|
||||||
MyAvatar.rotationThreshold = angleThresholdProperty.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertToRadians(num) {
|
|
||||||
return (num / DEGREES_PER_PI_RADIANS) * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertToDegrees(num) {
|
|
||||||
return (num / Math.PI) * DEGREES_PER_PI_RADIANS;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLog(x, y) {
|
|
||||||
return Math.log(y) / Math.log(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertLog(base, num, direction, shift) {
|
|
||||||
return direction * getLog(base, (num + FILTER_FUDGE_RANGE)) + shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertExponential(base, num, direction, shift) {
|
|
||||||
return Math.pow(base, (direction * num + shift)) - FILTER_FUDGE_RANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
function manageClick() {
|
|
||||||
if (activated) {
|
|
||||||
tablet.gotoHomeScreen();
|
|
||||||
} else {
|
|
||||||
tablet.gotoWebScreen(HTML_URL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tabletButton = tablet.addButton({
|
|
||||||
text: TABLET_BUTTON_NAME,
|
|
||||||
icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg"),
|
|
||||||
activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg")
|
|
||||||
});
|
|
||||||
|
|
||||||
function onKeyPress(event) {
|
|
||||||
if (event.text === "'") {
|
|
||||||
// when the sensors are reset, then reset the mode.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWebEventReceived(msg) {
|
|
||||||
var message = JSON.parse(msg);
|
|
||||||
print(" we have a message from html dialog " + message.type);
|
|
||||||
propArray.forEach(function (prop) {
|
|
||||||
if (prop.eventType === message.type) {
|
|
||||||
prop.setValue(prop.convertToThreshold(message.data.value));
|
|
||||||
print("message from " + prop.name);
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
checkBoxArray.forEach(function(cbox) {
|
|
||||||
if (cbox.eventType === message.type) {
|
|
||||||
cbox.data.value = message.data.value;
|
|
||||||
if (cbox.id === "stepReset") {
|
|
||||||
MyAvatar.enableStepResetRotation = cbox.data.value;
|
|
||||||
}
|
|
||||||
if (cbox.id === "drawAverage") {
|
|
||||||
MyAvatar.enableDrawAverageFacing = cbox.data.value;
|
|
||||||
}
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
if (message.type === "onCreateRotateApp") {
|
|
||||||
print("document loaded");
|
|
||||||
documentLoaded = true;
|
|
||||||
Script.setTimeout(initAppForm, LOADING_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initAppForm() {
|
|
||||||
print("step app is loaded: " + documentLoaded);
|
|
||||||
if (documentLoaded === true) {
|
|
||||||
propArray.forEach(function (prop) {
|
|
||||||
print(prop.name);
|
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
|
||||||
"type": "slider",
|
|
||||||
"id": prop.name,
|
|
||||||
"data": { "value": prop.convertToSlider(prop.value) }
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
checkBoxArray.forEach(function (cbox) {
|
|
||||||
tablet.emitScriptEvent(JSON.stringify({
|
|
||||||
"type": "checkboxtick",
|
|
||||||
"id": cbox.id,
|
|
||||||
"data": { value: cbox.data.value }
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onScreenChanged(type, url) {
|
|
||||||
print("Screen changed");
|
|
||||||
if (type === "Web" && url === HTML_URL) {
|
|
||||||
if (!activated) {
|
|
||||||
// hook up to event bridge
|
|
||||||
tablet.webEventReceived.connect(onWebEventReceived);
|
|
||||||
print("after connect web event");
|
|
||||||
MyAvatar.hmdLeanRecenterEnabled = true;
|
|
||||||
}
|
|
||||||
activated = true;
|
|
||||||
} else {
|
|
||||||
if (activated) {
|
|
||||||
// disconnect from event bridge
|
|
||||||
tablet.webEventReceived.disconnect(onWebEventReceived);
|
|
||||||
documentLoaded = false;
|
|
||||||
}
|
|
||||||
activated = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function limitAngle(angle) {
|
|
||||||
return (angle + 180) % 360 - 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeHandAzimuths(timeElapsed) {
|
|
||||||
|
|
||||||
// var leftHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.lhandPose.translation);
|
|
||||||
// var rightHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.rhandPose.translation);
|
|
||||||
|
|
||||||
|
|
||||||
// var hipToLeftHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: leftHandPositionRigSpace.x, y: 0, z: leftHandPositionRigSpace.z });
|
|
||||||
// var hipToRightHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: rightHandPositionRigSpace.x, y: 0, z: rightHandPositionRigSpace.z });
|
|
||||||
// var hipToHandHalfway = Quat.slerp(hipToLeftHand, hipToRightHand, 0.5);
|
|
||||||
|
|
||||||
var leftHand = currentStateReadings.lhandPose.translation;
|
|
||||||
var rightHand = currentStateReadings.rhandPose.translation;
|
|
||||||
var head = currentStateReadings.headPose.translation;
|
|
||||||
var lHandMinusHead = Vec3.subtract(leftHand, head);
|
|
||||||
lHandMinusHead.y = 0.0;
|
|
||||||
var rHandMinusHead = Vec3.subtract(rightHand, head);
|
|
||||||
rHandMinusHead.y = 0.0;
|
|
||||||
//print(JSON.stringify(leftHand));
|
|
||||||
//print(JSON.stringify(head));
|
|
||||||
var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 };
|
|
||||||
var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead);
|
|
||||||
var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead);
|
|
||||||
var tau = timeElapsed / filterLengthProperty.value;
|
|
||||||
hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, tau);
|
|
||||||
hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, tau);
|
|
||||||
|
|
||||||
// var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y);
|
|
||||||
// var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y);
|
|
||||||
var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHand).y + Quat.safeEulerAngles(hipToRHand).y) / 2.0;
|
|
||||||
var leftRightMidpointAverage = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0;
|
|
||||||
|
|
||||||
// limit the angle because we are flipped by 180, fix this tomorrow.
|
|
||||||
// print(leftRightMidpointAverage/180.0);
|
|
||||||
// print("threshold value " + angleThresholdProperty.value);
|
|
||||||
|
|
||||||
|
|
||||||
var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 });
|
|
||||||
var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 });
|
|
||||||
var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation);
|
|
||||||
// DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 });
|
|
||||||
|
|
||||||
//print(leftRightMidpoint);
|
|
||||||
|
|
||||||
var headPoseRigSpace = Quat.multiply(CHANGE_OF_BASIS_ROTATION, currentStateReadings.head.rotation);
|
|
||||||
headPoseAverageOrientation = Quat.slerp(headPoseAverageOrientation, headPoseRigSpace, tau);
|
|
||||||
var headPoseAverageEulers = Quat.safeEulerAngles(headPoseAverageOrientation);
|
|
||||||
|
|
||||||
// get it into radians too!!
|
|
||||||
// average head and hands 50/50
|
|
||||||
// print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y);
|
|
||||||
var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0;
|
|
||||||
if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) {
|
|
||||||
//print("recenter the feet under the head");
|
|
||||||
// MyAvatar.triggerRotationRecenter();
|
|
||||||
hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// put a hard max on this easing function.
|
|
||||||
var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint;
|
|
||||||
//print("rotate angle " + rotateAngle);
|
|
||||||
|
|
||||||
return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 });
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function update(dt) {
|
|
||||||
//update state readings
|
|
||||||
currentStateReadings.head = Controller.getPoseValue(Controller.Standard.Head);
|
|
||||||
currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand);
|
|
||||||
currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand);
|
|
||||||
|
|
||||||
//print(JSON.stringify(currentStateReadings.head));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var latestSpineRotation = computeHandAzimuths(dt);
|
|
||||||
|
|
||||||
spine2Rotation = latestSpineRotation;
|
|
||||||
var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2"));
|
|
||||||
spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos);
|
|
||||||
/*
|
|
||||||
if (HMD.active && !scriptLoaded) {
|
|
||||||
//Script.load("rotateApp.js");
|
|
||||||
scriptLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HMD.active) {
|
|
||||||
scriptLoaded = false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// handle the azimuth of the arms
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function shutdownTabletApp() {
|
|
||||||
tablet.removeButton(tabletButton);
|
|
||||||
if (activated) {
|
|
||||||
tablet.webEventReceived.disconnect(onWebEventReceived);
|
|
||||||
tablet.gotoHomeScreen();
|
|
||||||
}
|
|
||||||
tablet.screenChanged.disconnect(onScreenChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.setTimeout(function () {
|
|
||||||
tabletButton.clicked.connect(manageClick);
|
|
||||||
tablet.screenChanged.connect(onScreenChanged);
|
|
||||||
Script.update.connect(update);
|
|
||||||
Controller.keyPressEvent.connect(onKeyPress);
|
|
||||||
}, (LOADING_DELAY));
|
|
||||||
|
|
||||||
Script.scriptEnding.connect(function () {
|
|
||||||
// if (handlerId) {
|
|
||||||
// print("removing animation state handler");
|
|
||||||
// handlerId = MyAvatar.removeAnimationStateHandler(handlerId);
|
|
||||||
// }
|
|
||||||
MyAvatar.hmdLeanRecenterEnabled = true;
|
|
||||||
Script.update.disconnect(update);
|
|
||||||
shutdownTabletApp();
|
|
||||||
});
|
|
Loading…
Reference in a new issue