diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3a3176b78d..eefb654737 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -295,7 +295,7 @@ void AvatarMixer::broadcastAvatarData() { avatarPacketList.startSegment(); numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray()); + numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false)); avatarPacketList.endSegment(); diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 3d243d78ec..0b7af01c94 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -77,10 +77,7 @@ void ScriptableAvatar::update(float deltatime) { int mapping = animationJoints.indexOf(modelJoints[i]); if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { JointData& data = _jointData[i]; - data.valid = true; data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); - } else { - _jointData[i].valid = false; } } } else { diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index c80b492509..f6646e2272 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -47,13 +47,13 @@ if (WIN32) elseif(APPLE) # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32) elseif(NOT ANDROID) # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux32/libopenvr_api.so CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32) endif() diff --git a/examples/html/entityList.html b/examples/html/entityList.html index 62bbbd08a6..2e3fdb1874 100644 --- a/examples/html/entityList.html +++ b/examples/html/entityList.html @@ -20,7 +20,9 @@ elRefresh = document.getElementById("refresh"); elDelete = document.getElementById("delete"); elTeleport = document.getElementById("teleport"); + elRadius = document.getElementById("radius"); elNoEntitiesMessage = document.getElementById("no-entities"); + elNoEntitiesRadius = document.getElementById("no-entities-radius"); document.getElementById("entity-name").onclick = function() { setSortColumn('name'); @@ -186,6 +188,13 @@ } }, false); + elRadius.onchange = function () { + elRadius.value = Math.max(elRadius.value, 0); + EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value })); + refreshEntities(); + elNoEntitiesRadius.firstChild.nodeValue = elRadius.value; + } + if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); @@ -218,14 +227,15 @@
- - - + + +
+  m
@@ -246,7 +256,7 @@
- No entities found within 50 meter radius. Try moving to a different location and refreshing. + No entities found within a 100 meter radius. Try moving to a different location and refreshing.
diff --git a/examples/html/style.css b/examples/html/style.css index e63b6338fc..3614ea821b 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -102,13 +102,23 @@ input[type=button] { } #search-area { - width: 100%; padding: 0.5em; box-sizing: border-box; + padding-right: 6em; } -#search-area input { - width: 100%; +#filter { + width: 99%; +} + +#radius-and-unit { + width: 6em; + float: right; + margin-right: -6em; +} + +#radius { + width: 4em; } textarea, input { diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index 0fd1cd5a06..66dc9f336f 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -4,6 +4,8 @@ EntityListTool = function(opts) { var url = Script.resolvePath('html/entityList.html'); var webView = new WebWindow('Entities', url, 200, 280, true); + var searchRadius = 100; + var visible = false; webView.setVisible(visible); @@ -33,7 +35,7 @@ EntityListTool = function(opts) { that.sendUpdate = function() { var entities = []; - var ids = Entities.findEntities(MyAvatar.position, 100); + var ids = Entities.findEntities(MyAvatar.position, searchRadius); for (var i = 0; i < ids.length; i++) { var id = ids[i]; var properties = Entities.getEntityProperties(id); @@ -80,6 +82,9 @@ EntityListTool = function(opts) { } } else if (data.type == "delete") { deleteSelectedEntities(); + } else if (data.type === "radius") { + searchRadius = data.radius; + that.sendUpdate(); } }); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 854f6fefe9..8f6ce50eab 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -443,8 +443,6 @@ Menu::Menu() { SLOT(toggleConnexion(bool))); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); - addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); - addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandMouseInput, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c050e0b7a3..4a3059259b 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -131,8 +131,6 @@ namespace MenuOption { const QString AboutApp = "About Interface"; const QString AddRemoveFriends = "Add/Remove Friends..."; const QString AddressBar = "Show Address Bar"; - const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; - const QString AlternateIK = "Alternate IK"; const QString Animations = "Animations..."; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e7423336b1..55699bccbe 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -200,11 +200,9 @@ void Avatar::simulate(float deltaTime) { if (!_shouldRenderBillboard && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); - if (_hasNewJointRotations) { - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData.at(i); - _skeletonModel.setJointState(i, data.valid, data.rotation); - } + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + _skeletonModel.setJointState(i, true, data.rotation); } _skeletonModel.simulate(deltaTime, _hasNewJointRotations); simulateAttachments(deltaTime); @@ -784,8 +782,10 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const { bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); - // If we have nothing to draw, or it's tottaly transparent, return - if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { + // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return + const float CLIP_DISTANCE = 0.2f; + if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f + || (glm::dot(frustum.getDirection(), getDisplayNamePosition() - frustum.getPosition()) <= CLIP_DISTANCE)) { return; } auto renderer = textRenderer(DISPLAYNAME); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b72dd5c813..7d9d68714a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } -QByteArray MyAvatar::toByteArray() { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges) { CameraMode mode = Application::getInstance()->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = _position; _position = getSkeletonPosition(); - QByteArray array = AvatarData::toByteArray(); + QByteArray array = AvatarData::toByteArray(cullSmallChanges); // copy the correct position back _position = oldPosition; return array; } - return AvatarData::toByteArray(); + return AvatarData::toByteArray(cullSmallChanges); } void MyAvatar::reset() { @@ -220,7 +220,7 @@ void MyAvatar::simulate(float deltaTime) { _jointData.resize(_rig->getJointStateCount()); for (int i = 0; i < _jointData.size(); i++) { JointData& data = _jointData[i]; - data.valid = _rig->getJointStateRotation(i, data.rotation); + _rig->getJointStateRotation(i, data.rotation); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0283310e71..25e25e0960 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -203,7 +203,8 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(); + QByteArray toByteArray(bool cullSmallChanges); + void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0e29986bd1..367a956e5a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -41,7 +41,7 @@ SkeletonModel::~SkeletonModel() { void SkeletonModel::initJointStates(QVector states) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; int rootJointIndex = geometry.rootJointIndex; int leftHandJointIndex = geometry.leftHandJointIndex; @@ -51,7 +51,7 @@ void SkeletonModel::initJointStates(QVector states) { int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1; int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1; - _rig->initJointStates(states, parentTransform, + _rig->initJointStates(states, rootTransform, rootJointIndex, leftHandJointIndex, leftElbowJointIndex, @@ -83,7 +83,7 @@ void SkeletonModel::initJointStates(QVector states) { // of its root joint and we need that done before we try to build shapes hence we // recompute all joint transforms at this time. for (int i = 0; i < _rig->getJointStateCount(); i++) { - _rig->updateJointState(i, parentTransform); + _rig->updateJointState(i, rootTransform); } buildShapes(); @@ -157,11 +157,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients()); Model::simulate(deltaTime, fullUpdate); - + if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar } - + MyAvatar* myAvatar = static_cast(_owningAvatar); if (myAvatar->isPlaying()) { // Don't take inputs if playing back a recording. @@ -248,40 +248,24 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY); } - void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; int parentJointIndex = geometry.joints.at(jointIndex).parentIndex; if (parentJointIndex == -1) { return; } - - // rotate palm to align with its normal (normal points out of hand's palm) + + // the palm's position must be transformed into the model-frame glm::quat inverseRotation = glm::inverse(_rotation); glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation); - glm::vec3 palmNormal = inverseRotation * palm.getNormal(); - glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection(); - glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal); - palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation; + // the palm's "raw" rotation is already in the model-frame + glm::quat palmRotation = palm.getRawRotation(); - if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) { - _rig->setHandPosition(jointIndex, palmPosition, palmRotation, extractUniformScale(_scale), PALM_PRIORITY); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { - float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale); - glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f); - setJointPosition(parentJointIndex, palmPosition + forearm, - glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); - _rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY); - // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY); - } else { - inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); - } + inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { @@ -301,13 +285,13 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { _rotation : _rotation * _rig->getJointState(joint.parentIndex).getRotation(); float fanScale = directionSize * 0.75f; - + Transform transform = Transform(); transform.setTranslation(position); transform.setRotation(parentRotation); transform.setScale(fanScale); batch.setModelTransform(transform); - + const int AXIS_COUNT = 3; auto geometryCache = DependencyManager::get(); @@ -318,7 +302,7 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { } glm::vec3 axis; axis[i] = 1.0f; - + glm::vec3 otherAxis; if (i == 0) { otherAxis.y = 1.0f; @@ -339,18 +323,18 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { // better if the skeleton model cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_triangleFanID, points, color); geometryCache->renderVertices(batch, gpu::TRIANGLE_FAN, _triangleFanID); - + } - + renderOrientationDirections(batch, jointIndex, position, _rotation * jointState.getRotation(), directionSize); jointIndex = joint.parentIndex; - + } while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree); } -void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex, +void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex, glm::vec3 position, const glm::quat& orientation, float size) { - + auto geometryCache = DependencyManager::get(); if (!_jointOrientationLines.contains(jointIndex)) { @@ -486,7 +470,7 @@ void SkeletonModel::buildShapes() { if (_geometry == NULL || _rig->jointStatesEmpty()) { return; } - + const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton @@ -550,7 +534,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS, glm::vec4(0.6f, 0.6f, 0.8f, alpha)); - // draw a yellow sphere at the capsule bottom point + // draw a yellow sphere at the capsule bottom point glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f); glm::vec3 axis = topPoint - bottomPoint; transform.setTranslation(bottomPoint); diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index ebf0a8795e..0727d0956d 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -12,7 +12,7 @@ #include "AvatarRig.h" /// Updates the state of the joint at the specified index. -void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { +void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { if (index < 0 && index >= _jointStates.size()) { return; // bail } @@ -21,7 +21,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) { // compute model transforms if (index == _rootJointIndex) { // we always zero-out the translation part of an avatar's root join-transform. - state.computeTransform(parentTransform); + state.computeTransform(rootTransform); clearJointTransformTranslation(index); } else { // guard against out-of-bounds access to _jointStates diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h index 5e2153e226..f137e89939 100644 --- a/libraries/animation/src/AvatarRig.h +++ b/libraries/animation/src/AvatarRig.h @@ -21,7 +21,7 @@ class AvatarRig : public Rig { public: ~AvatarRig() {} - virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority); }; diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index eddbdd6464..e65407116b 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -12,13 +12,13 @@ #include "EntityRig.h" /// Updates the state of the joint at the specified index. -void EntityRig::updateJointState(int index, glm::mat4 parentTransform) { +void EntityRig::updateJointState(int index, glm::mat4 rootTransform) { JointState& state = _jointStates[index]; // compute model transforms int parentIndex = state.getParentIndex(); if (parentIndex == -1) { - state.computeTransform(parentTransform); + state.computeTransform(rootTransform); } else { // guard against out-of-bounds access to _jointStates if (parentIndex >= 0 && parentIndex < _jointStates.size()) { diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h index 9b519a7bfe..b36ffb9874 100644 --- a/libraries/animation/src/EntityRig.h +++ b/libraries/animation/src/EntityRig.h @@ -21,7 +21,7 @@ class EntityRig : public Rig { public: ~EntityRig() {} - virtual void updateJointState(int index, glm::mat4 parentTransform); + virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) {} }; diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 78da30fdcd..edea3b462d 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -153,6 +153,24 @@ void JointState::setRotationInBindFrame(const glm::quat& rotation, float priorit } } +void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain) { + // rotation is from bind- to model-frame + if (priority >= _animationPriority) { + glm::quat parentRotation = computeParentRotation(); + + // R = Rp * Rpre * r * Rpost + // R' = Rp * Rpre * r' * Rpost + // r' = (Rp * Rpre)^ * R' * Rpost^ + glm::quat targetRotation = glm::inverse(parentRotation * _preRotation) * rotationInModelFrame * glm::inverse(_postRotation); + if (constrain && _constraint) { + _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f); + } + _rotationInConstrainedFrame = glm::normalize(targetRotation); + _transformChanged = true; + _animationPriority = priority; + } +} + void JointState::clearTransformTranslation() { _transform[3][0] = 0.0f; _transform[3][1] = 0.0f; diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index 8d2c1a7cd0..4f45661eb2 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -82,6 +82,11 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false); + /// \param rotationInModelRame is in model-frame + /// computes and sets new _rotationInConstrainedFrame to match rotationInModelFrame + /// NOTE: the JointState's model-frame transform/rotation are NOT updated! + void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain); + void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 356afe6620..4f0257b4dc 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -194,7 +194,7 @@ void Rig::deleteAnimations() { } } -void Rig::initJointStates(QVector states, glm::mat4 parentTransform, +void Rig::initJointStates(QVector states, glm::mat4 rootTransform, int rootJointIndex, int leftHandJointIndex, int leftElbowJointIndex, @@ -212,7 +212,7 @@ void Rig::initJointStates(QVector states, glm::mat4 parentTransform, _rightElbowJointIndex = rightElbowJointIndex; _rightShoulderJointIndex = rightShoulderJointIndex; - initJointTransforms(parentTransform); + initJointTransforms(rootTransform); int numStates = _jointStates.size(); for (int i = 0; i < numStates; ++i) { @@ -235,14 +235,14 @@ int Rig::indexOfJoint(const QString& jointName) { } -void Rig::initJointTransforms(glm::mat4 parentTransform) { +void Rig::initJointTransforms(glm::mat4 rootTransform) { // compute model transforms int numStates = _jointStates.size(); for (int i = 0; i < numStates; ++i) { JointState& state = _jointStates[i]; int parentIndex = state.getParentIndex(); if (parentIndex == -1) { - state.initTransform(parentTransform); + state.initTransform(rootTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); state.initTransform(parentState.getTransform()); @@ -466,7 +466,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastPosition = worldPosition; } -void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { +void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { if (_enableAnimGraph) { if (!_animNode) { @@ -489,7 +489,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { } for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, parentTransform); + updateJointState(i, rootTransform); } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].resetTransformChanged(); @@ -541,7 +541,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { } for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, parentTransform); + updateJointState(i, rootTransform); } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].resetTransformChanged(); @@ -551,7 +551,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, - const QVector& freeLineage, glm::mat4 parentTransform) { + const QVector& freeLineage, glm::mat4 rootTransform) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -603,7 +603,7 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm: glm::vec3 positionSum; for (int k = j - 1; k > 0; k--) { int index = freeLineage.at(k); - updateJointState(index, parentTransform); + updateJointState(index, rootTransform); positionSum += extractTranslation(_jointStates.at(index).getTransform()); } glm::vec3 projectedCenterOfMass = glm::cross(jointVector, @@ -626,15 +626,15 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm: // now update the joint states from the top for (int j = freeLineage.size() - 1; j >= 0; j--) { - updateJointState(freeLineage.at(j), parentTransform); + updateJointState(freeLineage.at(j), rootTransform); } return true; } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, - const QVector& freeLineage, glm::mat4 parentTransform) { - // NOTE: targetRotation is from bind- to model-frame + const QVector& freeLineage, glm::mat4 rootTransform) { + // NOTE: targetRotation is from in model-frame if (endIndex == -1 || _jointStates.isEmpty()) { return; @@ -652,12 +652,27 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q const JointState& state = _jointStates.at(index); int parentIndex = state.getParentIndex(); if (parentIndex == -1) { - topParentTransform = parentTransform; + topParentTransform = rootTransform; } else { topParentTransform = _jointStates[parentIndex].getTransform(); } } + // relax toward default rotation + // NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax + for (int j = 0; j < numFree; j++) { + int nextIndex = freeLineage.at(j); + JointState& nextState = _jointStates[nextIndex]; + if (! nextState.getIsFree()) { + continue; + } + + // Apply the zero rotationDelta, but use mixRotationDelta() which blends a bit of the default pose + // in the process. This provides stability to the IK solution for most models. + float mixFactor = 0.08f; + nextState.mixRotationDelta(glm::quat(), mixFactor, priority); + } + // this is a cyclic coordinate descent algorithm: see // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d @@ -666,7 +681,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q glm::vec3 endPosition = endState.getPosition(); float distanceToGo = glm::distance(targetPosition, endPosition); - const int MAX_ITERATION_COUNT = 2; + const int MAX_ITERATION_COUNT = 3; const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm int numIterations = 0; do { @@ -704,7 +719,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q float gravityAngle = glm::angle(gravityDelta); const float MIN_GRAVITY_ANGLE = 0.1f; - float mixFactor = 0.5f; + float mixFactor = 0.1f; if (gravityAngle < MIN_GRAVITY_ANGLE) { // the final rotation is a mix of the two mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; @@ -712,11 +727,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); } - // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose - // in the process. This provides stability to the IK solution for most models. + // Apply the rotation delta. glm::quat oldNextRotation = nextState.getRotation(); - float mixFactor = 0.03f; - nextState.mixRotationDelta(deltaRotation, mixFactor, priority); + float mixFactor = 0.05f; + nextState.applyRotationDelta(deltaRotation, mixFactor, priority); // measure the result of the rotation which may have been modified by // blending and constraints @@ -735,10 +749,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q // measure our success endPosition = endState.getPosition(); distanceToGo = glm::distance(targetPosition, endPosition); - } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); + } while (numIterations < MAX_ITERATION_COUNT && distanceToGo > ACCEPTABLE_IK_ERROR); // set final rotation of the end joint - endState.setRotationInBindFrame(targetRotation, priority, true); + endState.setRotationInModelFrame(targetRotation, priority, true); } bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1b7bf72e88..845878f8a2 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -95,7 +95,7 @@ public: float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false); - void initJointStates(QVector states, glm::mat4 parentTransform, + void initJointStates(QVector states, glm::mat4 rootTransform, int rootJointIndex, int leftHandJointIndex, int leftElbowJointIndex, @@ -107,7 +107,7 @@ public: int getJointStateCount() const { return _jointStates.size(); } int indexOfJoint(const QString& jointName) ; - void initJointTransforms(glm::mat4 parentTransform); + void initJointTransforms(glm::mat4 rootTransform); void clearJointTransformTranslation(int jointIndex); void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; @@ -138,12 +138,12 @@ public: // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); // Regardless of who started the animations or how many, update the joints. - void updateAnimations(float deltaTime, glm::mat4 parentTransform); + void updateAnimations(float deltaTime, glm::mat4 rootTransform); bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, - const QVector& freeLineage, glm::mat4 parentTransform); + const QVector& freeLineage, glm::mat4 rootTransform); void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, - const QVector& freeLineage, glm::mat4 parentTransform); + const QVector& freeLineage, glm::mat4 rootTransform); bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); float getLimbLength(int jointIndex, const QVector& freeLineage, const glm::vec3 scale, const QVector& fbxJoints) const; @@ -155,7 +155,7 @@ public: glm::quat getJointDefaultRotationInParentFrame(int jointIndex); void updateVisibleJointStates(); - virtual void updateJointState(int index, glm::mat4 parentTransform) = 0; + virtual void updateJointState(int index, glm::mat4 rootTransform) = 0; void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9ef1f94c55..123c9707ba 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -31,6 +31,10 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; +// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer +const float MIN_ROTATION_DOT = 0.9999999f; + + using namespace std; const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f); @@ -141,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - _position); } -QByteArray AvatarData::toByteArray() { +QByteArray AvatarData::toByteArray(bool cullSmallChanges) { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -234,11 +238,19 @@ QByteArray AvatarData::toByteArray() { // joint data *destinationBuffer++ = _jointData.size(); + unsigned char* validityPosition = destinationBuffer; unsigned char validity = 0; int validityBit = 0; - foreach (const JointData& data, _jointData) { - if (data.valid) { - validity |= (1 << validityBit); + + _lastSentJointData.resize(_jointData.size()); + + // foreach (const JointData& data, _jointData) { + for (int i=0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + if (_lastSentJointData[i].rotation != data.rotation) { + if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { + validity |= (1 << validityBit); + } } if (++validityBit == BITS_IN_BYTE) { *destinationBuffer++ = validity; @@ -248,9 +260,18 @@ QByteArray AvatarData::toByteArray() { if (validityBit != 0) { *destinationBuffer++ = validity; } - foreach (const JointData& data, _jointData) { - if (data.valid) { + + validityBit = 0; + validity = *validityPosition++; + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[ i ]; + if (validity & (1 << validityBit)) { destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); + _lastSentJointData[i].rotation = data.rotation; + } + if (++validityBit == BITS_IN_BYTE) { + validityBit = 0; + validity = *validityPosition++; } } @@ -494,6 +515,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } int numValidJoints = 0; _jointData.resize(numJoints); + + QVector valids; + valids.resize(numJoints); + { // validity bits unsigned char validity = 0; int validityBit = 0; @@ -505,7 +530,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (valid) { ++numValidJoints; } - _jointData[i].valid = valid; + valids[i] = valid; validityBit = (validityBit + 1) % BITS_IN_BYTE; } } @@ -527,7 +552,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { { // joint data for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; - if (data.valid) { + if (valids[i]) { _hasNewJointRotations = true; sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } @@ -731,7 +756,6 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) { _jointData.resize(index + 1); } JointData& data = _jointData[index]; - data.valid = true; data.rotation = rotation; } @@ -746,7 +770,6 @@ void AvatarData::clearJointData(int index) { if (_jointData.size() <= index) { _jointData.resize(index + 1); } - _jointData[index].valid = false; } bool AvatarData::isJointDataValid(int index) const { @@ -759,7 +782,7 @@ bool AvatarData::isJointDataValid(int index) const { Q_RETURN_ARG(bool, result), Q_ARG(int, index)); return result; } - return index < _jointData.size() && _jointData.at(index).valid; + return index < _jointData.size(); } glm::quat AvatarData::getJointRotation(int index) const { @@ -1060,7 +1083,7 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(); + QByteArray avatarByteArray = toByteArray(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8bb874bc71..278ec2047c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -171,7 +171,7 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(); + virtual QByteArray toByteArray(bool cullSmallChanges); /// \return true if an error should be logged bool shouldLogError(const quint64& now); @@ -357,6 +357,7 @@ protected: char _handState; QVector _jointData; ///< the state of the skeleton joints + QVector _lastSentJointData; ///< the state of the skeleton joints last time we transmitted // key state KeyState _keyState; @@ -408,7 +409,6 @@ Q_DECLARE_METATYPE(AvatarData*) class JointData { public: - bool valid; glm::quat rotation; }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index f99d1f2a3b..4101bebfa8 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -104,10 +104,10 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) { return true; } + break; default: break; } - return false; } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 9e45efe88d..7dab825adc 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -131,20 +131,20 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x; float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x; - float maxXDistance = (maxVelocityX * time) + (0.5 * maxAccelerationX * time * time); + float maxXDistance = (maxVelocityX * time) + (0.5f * maxAccelerationX * time * time); - float maxVelocityY = fabs(_velocity.y) + _velocitySpread.y; + float maxVelocityY = fabsf(_velocity.y) + _velocitySpread.y; float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y; - float maxYDistance = (maxVelocityY * time) + (0.5 * maxAccelerationY * time * time); + float maxYDistance = (maxVelocityY * time) + (0.5f * maxAccelerationY * time * time); float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z; float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z; - float maxZDistance = (maxVelocityZ * time) + (0.5 * maxAccelerationZ * time * time); + float maxZDistance = (maxVelocityZ * time) + (0.5f * maxAccelerationZ * time * time); float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance)); //times 2 because dimensions are diameters not radii - glm::vec3 dims(2.0 * maxDistance); + glm::vec3 dims(2.0f * maxDistance); EntityItem::setDimensions(dims); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 205350e751..579fddcd63 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -21,6 +21,10 @@ #include "SixenseManager.h" #include "UserActivityLogger.h" +#ifdef HAVE_SIXENSE + #include "sixense.h" +#endif + // TODO: This should not be here #include Q_DECLARE_LOGGING_CATEGORY(inputplugins) @@ -45,12 +49,14 @@ const float NECK_Z = 0.3f; // meters const float CONTROLLER_THRESHOLD = 0.35f; +#endif + #ifdef __APPLE__ typedef int (*SixenseBaseFunction)(); typedef int (*SixenseTakeIntFunction)(int); +#ifdef HAVE_SIXENSE typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*); #endif - #endif const QString SixenseManager::NAME = "Sixense"; @@ -66,8 +72,8 @@ SixenseManager& SixenseManager::getInstance() { } SixenseManager::SixenseManager() : - InputDevice("Hydra"), -#if defined(HAVE_SIXENSE) && defined(__APPLE__) + InputDevice("Hydra"), +#ifdef __APPLE__ _sixenseLibrary(NULL), #endif _hydrasConnected(false) @@ -213,18 +219,16 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); position *= METERS_PER_MILLIMETER; - + // Check to see if this hand/controller is on the base const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { handleButtonEvent(data->buttons, numActiveControllers - 1); handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); - // Rotation of Palm - glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); - rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation; - if (!jointsCaptured) { + // Rotation of Palm + glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); handlePoseEvent(position, rotation, numActiveControllers - 1); } else { _poseStateMap.clear(); @@ -232,7 +236,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { } else { _poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue(); } - + // // Read controller buttons and joystick into the hand // palm->setControllerButtons(data->buttons); // palm->setTrigger(data->trigger); @@ -242,7 +246,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { if (numActiveControllers == 2) { updateCalibration(controllers); } - + for (auto axisState : _axisStateMap) { if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { _axisStateMap[axisState.first] = 0.0f; @@ -436,16 +440,66 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) { #ifdef HAVE_SIXENSE + // From ABOVE the sixense coordinate frame looks like this: + // + // | + // USB cables + // | + // .-. user + // (Orb) --neckX---- forward + // '-' | + // | | user + // neckZ y +---- right + // | (o)-----x + // | + // | + // z + // Transform the measured position into body frame. glm::vec3 neck = _neckBase; // Set y component of the "neck" to raise the measured position a little bit. neck.y = 0.5f; position = _orbRotation * (position - neck); - - // adjustment for hydra controllers fit into hands - float sign = (index == 0) ? -1.0f : 1.0f; - rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f)); - + + // From ABOVE the hand canonical axes looks like this: + // + // | | | | y | | | | + // | | | | | | | | | + // | | | | | + // |left | / x----(+) \ |right| + // | _/ z \_ | + // | | | | + // | | | | + // + + // To convert sixense's delta-rotation into the hand's frame we will have to transform it like so: + // + // deltaHand = Qsh^ * deltaSixense * Qsh + // + // where Qsh = transform from sixense axes to hand axes. By inspection we can determine Qsh: + // + // Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis) + // + const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f); + const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f); + const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis); + + // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers + // and how they fit into the hand in their relaxed state. This offset is a quarter turn about + // the sixense's z-axis, with its direction different for the two hands: + float sign = (index == 0) ? 1.0f : -1.0f; + const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis); + + // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation + // (fingers forward, palm down) aligned properly in the avatar's model-frame. + const glm::quat postOffset = glm::angleAxis(PI / 2.0f, xAxis); + + // The total rotation of the hand uses the formula: + // + // rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh + // + rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; + _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); #endif // HAVE_SIXENSE } @@ -453,7 +507,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); - + auto proxy = std::make_shared(_name); proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; @@ -465,25 +519,25 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1")); availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3")); availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1")); availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down")); availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right")); @@ -506,35 +560,35 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { const float JOYSTICK_PITCH_SPEED = 0.25f; const float BUTTON_MOVE_SPEED = 1.0f; const float BOOM_SPEED = 0.1f; - + // Left Joystick: Movement, strafing mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED); - + // Right Joystick: Camera orientation mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED); mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED); mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED); mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED); - + // Buttons mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); - + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); - + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); - + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0)); mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); - + mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); - + mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index fabb488ab4..1b9b87684c 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -230,7 +230,7 @@ void UserInputMapper::update(float deltaTime) { for (auto i = 0; i < NUM_ACTIONS; i++) { _actionStates[i] *= _actionScales[i]; // Emit only on change, and emit when moving back to 0 - if (fabs(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { _lastActionStates[i] = _actionStates[i]; emit actionEvent(i, _actionStates[i]); } @@ -319,4 +319,4 @@ void UserInputMapper::createActionNames() { _actionNames[SHIFT] = "SHIFT"; _actionNames[ACTION1] = "ACTION1"; _actionNames[ACTION2] = "ACTION2"; -} \ No newline at end of file +} diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 3aef37e502..082c37a837 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -324,9 +324,64 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::quat_cast(mat); - // Flip the rotation appropriately for each hand - int sign = index == LEFT_HAND ? 1 : -1; - rotation = rotation * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); + // When the sensor-to-world rotation is identity the coordinate axes look like this: + // + // user + // forward + // z + // | + // y| user + // y o----x right + // o-----x user + // | up + // | + // z + // + // Vive + // + + // From ABOVE the hand canonical axes looks like this: + // + // | | | | y | | | | + // | | | | | | | | | + // | | | | | + // |left | / x---- + \ |right| + // | _/ z \_ | + // | | | | + // | | | | + // + + // So when the user is standing in Vive space facing the -zAxis with hands outstretched and palms down + // the rotation to align the Vive axes with those of the hands is: + // + // QviveToHand = halfTurnAboutY * quaterTurnAboutX + + // Due to how the Vive controllers fit into the palm there is an offset that is different for each hand. + // You can think of this offset as the inverse of the measured rotation when the hands are posed, such that + // the combination (measurement * offset) is identity at this orientation. + // + // Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down) + // + // An approximate offset for the Vive can be obtained by inpection: + // + // Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis)) + // + + // Finally there is another flip around the yAxis to re-align from model to Vive space, so the full equation is: + // + // Q = yFlip * combinedMeasurement * viveToHand + // + // Q = yFlip * (deltaQ * QOffset) * (yFlip * quarterTurnAboutX) + // + // Q = yFlip * (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX) + + const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + float sign = (index == LEFT_HAND) ? -1.0f : 1.0f; + const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); + const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::quat offset = glm::inverse(signedQuaterZ * eighthX); + rotation = yFlip * rotation * offset * yFlip * quarterX; position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 7e73b4c660..ac72f1bd68 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -69,7 +69,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityData: return VERSION_ENTITIES_PARTICLE_MODIFICATIONS; case AvatarData: - return 12; + return 13; default: return 11; } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 64f3bb6708..ebd2053442 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -243,7 +243,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight); - batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight); + batch._glUniform2f(_renderTargetResInvLoc, 1.0f / fbWidth, 1.0f / fbHeight); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b42daa710a..aa395b1b06 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -599,7 +599,7 @@ void ScriptEngine::run() { / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); - QByteArray avatarByteArray = _avatarData->toByteArray(); + QByteArray avatarByteArray = _avatarData->toByteArray(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index cd6f97e615..49e5a68a69 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -372,20 +372,20 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { // create matrix from orientation and position glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p) { glm::mat4 m = glm::mat4_cast(q); - m[3] = glm::vec4(p, 1); + m[3] = glm::vec4(p, 1.0f); return m; } // cancel out roll and pitch glm::quat cancelOutRollAndPitch(const glm::quat& q) { - glm::vec3 zAxis = q * glm::vec3(0, 0, 1); + glm::vec3 zAxis = q * glm::vec3(0.0f, 0.0f, 1.0f); // cancel out the roll and pitch - glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z)); - glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ); + glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z)); + glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ); glm::vec3 newY = glm::cross(newZ, newX); - glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), glm::vec4(0, 0, 0, 1)); + glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); return glm::quat_cast(temp); } @@ -394,16 +394,16 @@ glm::mat4 cancelOutRollAndPitch(const glm::mat4& m) { glm::vec3 zAxis = glm::vec3(m[2]); // cancel out the roll and pitch - glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z)); - glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ); + glm::vec3 newZ = (zAxis.x == 0.0f && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z)); + glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ); glm::vec3 newY = glm::cross(newZ, newX); - glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), m[3]); + glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), m[3]); return temp; } glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) { - glm::vec4 temp = m * glm::vec4(p, 1); + glm::vec4 temp = m * glm::vec4(p, 1.0f); return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w); } diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp index 740d401107..a94bda9a86 100644 --- a/tests/entities/src/main.cpp +++ b/tests/entities/src/main.cpp @@ -93,8 +93,8 @@ template void testByteCountCoded() { testByteCountCodedStable(0); testByteCountCodedStable(1); - testByteCountCodedStable(1 << 16); - testByteCountCodedStable(std::numeric_limits::max() >> 16); + testByteCountCodedStable(1 << 8*sizeof(T)); + testByteCountCodedStable(std::numeric_limits::max() >> 8*sizeof(T)); testByteCountCodedStable(std::numeric_limits::max() >> 8); testByteCountCodedStable(std::numeric_limits::max() >> 1); testByteCountCodedStable(std::numeric_limits::max()); diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 758d9b29bb..b27d10e312 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -342,8 +342,8 @@ public: glm::vec3 unitscale { 1.0f }; glm::vec3 up { 0.0f, 1.0f, 0.0f }; - glm::vec3 cam_pos { 1.5f * sin(t), 0.0f, 2.0f }; -// glm::vec3 camera_focus { 5.0f * cos(t * 0.1f), 0.0f, 0.0f }; + glm::vec3 cam_pos { 1.5f * sinf(t), 0.0f, 2.0f }; +// glm::vec3 camera_focus { 5.0f * cosf(t * 0.1f), 0.0f, 0.0f }; glm::vec3 camera_focus { 0.0f, 0.0f, 0.0f }; glm::quat cam_rotation; // glm::quat cam_rotation = glm::quat_cast(glm::lookAt(cam_pos, camera_focus, up));