From 2046608fa8bbfe5bbba40718139081d53d54fd35 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 10 Jul 2014 16:16:54 -0700 Subject: [PATCH 01/18] enforce alphabetical sort of Menu option strings --- interface/src/Menu.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4ca50f71b8..7a261fb7fb 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -305,13 +305,14 @@ namespace MenuOption { const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; const QString AudioScope = "Audio Scope"; - const QString AudioScopePause = "Pause Audio Scope"; - const QString AudioScopeFrames = "Display Frames"; - const QString AudioScopeFiveFrames = "Five"; - const QString AudioScopeTwentyFrames = "Twenty"; const QString AudioScopeFiftyFrames = "Fifty"; - const QString AudioToneInjection = "Inject Test Tone"; + const QString AudioScopeFiveFrames = "Five"; + const QString AudioScopeFrames = "Display Frames"; + const QString AudioScopePause = "Pause Audio Scope"; + const QString AudioScopeTwentyFrames = "Twenty"; + const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation"; const QString AudioSpatialProcessing = "Audio Spatial Processing"; + const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation"; const QString AudioSpatialProcessingHeadOriented = "Head Oriented"; const QString AudioSpatialProcessingIncludeOriginal = "Includes Network Original"; const QString AudioSpatialProcessingPreDelay = "Add Pre-Delay"; @@ -321,14 +322,12 @@ namespace MenuOption { const QString AudioSpatialProcessingSlightlyRandomSurfaces = "Slightly Random Surfaces"; const QString AudioSpatialProcessingStereoSource = "Stereo Source"; const QString AudioSpatialProcessingWithDiffusions = "With Diffusions"; - const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation"; - const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation"; + const QString AudioToneInjection = "Inject Test Tone"; const QString Avatars = "Avatars"; const QString AvatarsReceiveShadows = "Avatars Receive Shadows"; const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; const QString BuckyBalls = "Bucky Balls"; - const QString StringHair = "String Hair"; const QString CascadedShadows = "Cascaded"; const QString Chat = "Chat..."; const QString ChatCircling = "Chat Circling"; @@ -348,26 +347,25 @@ namespace MenuOption { const QString DisplayHands = "Display Hands"; const QString DisplayHandTargets = "Display Hand Targets"; const QString DisplayModelBounds = "Display Model Bounds"; - const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; + const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayTimingDetails = "Display Timing Details"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; - const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString EnableVRMode = "Enable VR Mode"; - const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing"; - const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing"; const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing"; + const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing"; const QString ExpandDisplaySideTiming = "Expand Display Side Timing"; const QString ExpandIdleTiming = "Expand Idle Timing"; + const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing"; const QString ExpandPaintGLTiming = "Expand PaintGL Timing"; const QString ExpandUpdateTiming = "Expand Update Timing"; const QString Faceplus = "Faceplus"; const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; - const QString LowVelocityFilter = "Low Velocity Filter"; const QString FirstPerson = "First Person"; const QString FocusIndicators = "Focus Indicators"; const QString FrameTimer = "Show Timer"; @@ -377,10 +375,9 @@ namespace MenuOption { const QString GlowMode = "Cycle Glow Mode"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString GoHome = "Go Home"; - const QString GoTo = "Go To..."; const QString GoToDomain = "Go To Domain..."; + const QString GoTo = "Go To..."; const QString GoToLocation = "Go To Location..."; - const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString HandsCollideWithSelf = "Collide With Self"; const QString HeadMouse = "Head Mouse"; const QString IncreaseAvatarSize = "Increase Avatar Size"; @@ -388,21 +385,23 @@ namespace MenuOption { const QString LoadScript = "Open and Run Script File..."; const QString LoadScriptURL = "Open and Run Script from URL..."; const QString LodTools = "LOD Tools"; - const QString Log = "Log"; const QString Login = "Login"; + const QString Log = "Log"; const QString Logout = "Logout"; const QString LookAtVectors = "Look-at Vectors"; + const QString LowVelocityFilter = "Low Velocity Filter"; const QString MetavoxelEditor = "Metavoxel Editor..."; const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; - const QString Models = "Models"; const QString ModelOptions = "Model Options"; + const QString Models = "Models"; const QString MoveWithLean = "Move with Lean"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; const QString MyLocations = "My Locations..."; const QString NameLocation = "Name this location"; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; + const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString OctreeStats = "Voxel and Particle Statistics"; const QString OffAxisProjection = "Off-Axis Projection"; const QString OldVoxelCullingMode = "Old Voxel Culling Mode"; @@ -422,17 +421,18 @@ namespace MenuOption { const QString ScriptEditor = "Script Editor..."; const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; - const QString SimpleShadows = "Simple"; - const QString SixenseMouseInput = "Enable Sixense Mouse Input"; - const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes"; + const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowIKConstraints = "Show IK Constraints"; + const QString SimpleShadows = "Simple"; + const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString StandOnNearbyFloors = "Stand on nearby floors"; const QString Stars = "Stars"; const QString Stats = "Stats"; const QString StereoAudio = "Stereo Audio"; const QString StopAllScripts = "Stop All Scripts"; + const QString StringHair = "String Hair"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; From fb287f71e85c8f0885ccd84c550b21992a8736e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 10 Jul 2014 16:17:55 -0700 Subject: [PATCH 02/18] add more methods for processing visible transforms --- interface/src/renderer/JointState.cpp | 21 +++++++++++++++++++++ interface/src/renderer/JointState.h | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 5a2766d39f..7958e8b6cd 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -164,12 +164,27 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float setRotationInConstrainedFrame(targetRotation); } +void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) { + // NOTE: delta is in model-frame + assert(_fbxJoint != NULL); + glm::quat targetRotation = _visibleRotationInConstrainedFrame * glm::inverse(_rotation) * delta * _rotation; + if (mixFactor > 0.0f && mixFactor <= 1.0f) { + //targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); + targetRotation = safeMix(targetRotation, _rotationInConstrainedFrame, mixFactor); + } + setVisibleRotationInConstrainedFrame(targetRotation); +} + glm::quat JointState::computeParentRotation() const { // R = Rp * Rpre * r * Rpost // Rp = R * (Rpre * r * Rpost)^ return _rotation * glm::inverse(_fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation); } +glm::quat JointState::computeVisibleParentRotation() const { + return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation); +} + void JointState::setRotationInConstrainedFrame(const glm::quat& targetRotation) { glm::quat parentRotation = computeParentRotation(); _rotationInConstrainedFrame = targetRotation; @@ -177,6 +192,12 @@ void JointState::setRotationInConstrainedFrame(const glm::quat& targetRotation) _rotation = parentRotation * _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; } +void JointState::setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation) { + glm::quat parentRotation = computeVisibleParentRotation(); + _visibleRotationInConstrainedFrame = targetRotation; + _visibleRotation = parentRotation * _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; +} + const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const { assert(_fbxJoint != NULL); return _fbxJoint->translation; diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 049eb6e6b0..36a0212b45 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -47,6 +47,8 @@ public: /// \return rotation from bind to model frame glm::quat getRotationFromBindToModelFrame() const; + int getParentIndex() const { return _fbxJoint->parentIndex; } + /// \param rotation rotation of joint in model-frame void setRotation(const glm::quat& rotation, bool constrain, float priority); @@ -59,6 +61,7 @@ public: /// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) /// \param priority priority level of this animation blend void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f); + void mixVisibleRotationDelta(const glm::quat& delta, float mixFactor); /// Blends a fraciton of default pose into joint rotation. /// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) @@ -71,6 +74,7 @@ public: void setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain = false); void setRotationInConstrainedFrame(const glm::quat& targetRotation); + void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } const glm::vec3& getDefaultTranslationInConstrainedFrame() const; @@ -86,6 +90,7 @@ private: /// \return parent model-frame rotation // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) glm::quat computeParentRotation() const; + glm::quat computeVisibleParentRotation() const; /// debug helper function void loadBindRotation(); From b012c0a008f805dc6829bd02050ac96b06cc54c2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 10 Jul 2014 16:19:27 -0700 Subject: [PATCH 03/18] update visible transforms right before render --- interface/src/renderer/Model.cpp | 14 ++++++++------ interface/src/renderer/Model.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index cbdbff072b..5fec542c6e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -39,7 +39,7 @@ Model::Model(QObject* parent) : _scaledToFit(false), _snapModelToCenter(false), _snappedToCenter(false), - _showTrueJointTransforms(false), + _showTrueJointTransforms(true), _rootIndex(-1), _lodDistance(0.0f), _pupilDilation(0.0f), @@ -937,7 +937,6 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } - updateVisibleJointStates(); _shapesAreDirty = ! _shapes.isEmpty(); @@ -1006,10 +1005,12 @@ void Model::updateJointState(int index) { } void Model::updateVisibleJointStates() { - if (!_showTrueJointTransforms) { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].slaveVisibleTransform(); - } + if (_showTrueJointTransforms) { + // no need to update visible transforms + return; + } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); } } @@ -1356,6 +1357,7 @@ void Model::deleteGeometry() { } void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows) { + updateVisibleJointStates(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 9e2e0d8348..dde3a68218 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -148,6 +148,8 @@ public: void setLocalLightDirection(const glm::vec3& direction, int lightIndex); void setLocalLightColor(const glm::vec3& color, int lightIndex); void setNumLocalLights(int numLocalLights); + + void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } protected: QSharedPointer _geometry; From 7e4efa6c730d8a57dc2c28721062e33d22d5205c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 10 Jul 2014 16:21:12 -0700 Subject: [PATCH 04/18] Skeleton gets custom update for visible transforms --- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/avatar/SkeletonModel.cpp | 39 ++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04e7b628c9..746ac72ab2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -135,6 +135,7 @@ void MyAvatar::simulate(float deltaTime) { setScale(scale); Application::getInstance()->getCamera()->setScale(scale); } + _skeletonModel.setShowTrueJointTransforms(! Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)); // no extra movement of the hand here any more ... _handState = HAND_STATE_NULL; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index d9b2340a90..f4f2a33331 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -537,8 +537,43 @@ void SkeletonModel::buildRagdollConstraints() { } void SkeletonModel::updateVisibleJointStates() { - Model::updateVisibleJointStates(); - // TODO: implement this to move visible joints to agree with joint shape positions + if (_showTrueJointTransforms) { + // no need to update visible transforms + return; + } + for (int i = 0; i < _jointStates.size(); i++) { + JointState& state = _jointStates[i]; + + // get the parent state (this is the state that we want to rotate) + int parentIndex = state.getParentIndex(); + if (parentIndex == -1) { + _jointStates[i].slaveVisibleTransform(); + continue; + } + JointState& parentState = _jointStates[parentIndex]; + + // check the grand-parent index (for now we don't want to rotate any root states) + int grandParentIndex = parentState.getParentIndex(); + if (grandParentIndex == -1) { + continue; + } + + // make sure state's visibleTransform is up to date + const glm::mat4& parentTransform = parentState.getVisibleTransform(); + state.computeVisibleTransform(parentTransform); + + // we're looking for the rotation that moves visible bone parallel to ragdoll bone + glm::vec3 pivot = extractTranslation(parentTransform); + glm::vec3 tip = state.getVisiblePosition(); + glm::vec3 shapeTip = _ragdollPoints[i]._position; + glm::quat delta = rotationBetween(tip - pivot, shapeTip - pivot); + + // apply + parentState.mixVisibleRotationDelta(delta, 0.01f); + // update transforms + parentState.computeVisibleTransform(_jointStates[grandParentIndex].getVisibleTransform()); + state.computeVisibleTransform(parentState.getVisibleTransform()); + } } // virtual From 7c8f5e2c127ac16d7e85a69a3d5b5c15dac56749 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 11 Jul 2014 10:54:03 -0700 Subject: [PATCH 05/18] Remove some old TODO comments --- libraries/shared/src/PhysicsSimulation.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index e2f95a213c..ca9d3d303a 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -126,22 +126,7 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { } } } -// TODO: Andrew to implement: -// DONE (1) joints pull points (SpecialCapsuleShape would help solve this) -// DONE (2) points slam shapes (SpecialCapsuleShape would help solve this) -// DONE (3) detect collisions -// DONE (4) collisions move points (SpecialCapsuleShape would help solve this) -// DONE (5) enforce constraints -// DONE (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support -// DONE (7) support for pairwise collision bypass -// DONE (8) process collisions -// DONE (8a) stubbery -// DONE (8b) shapes actually accumulate movement -// DONE (9) verify that avatar shapes self collide -// (10) slave rendered SkeletonModel to physical shapes -// (10a) give SkeletonModel duplicate JointState data -// (10b) figure out how to slave dupe JointStates to physical shapes -// (11) add and enforce angular contraints for joints + void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { quint64 now = usecTimestampNow(); quint64 startTime = now; From c5a5f8c5d465ac8d1e7f4fd36a41d83c218853a0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 11 Jul 2014 10:54:16 -0700 Subject: [PATCH 06/18] bug fix: bad logic in capsule-vs-capsule --- libraries/shared/src/ShapeCollider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index ffb51660e2..03c275d987 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -364,12 +364,12 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, // clamp the distances to the ends of the capsule line segments float absDistanceA = fabs(distanceA); - if (absDistanceA > capsuleA->getHalfHeight() + capsuleA->getRadius()) { + if (absDistanceA > capsuleA->getHalfHeight()) { float signA = distanceA < 0.0f ? -1.0f : 1.0f; distanceA = signA * capsuleA->getHalfHeight(); } float absDistanceB = fabs(distanceB); - if (absDistanceB > capsuleB->getHalfHeight() + capsuleB->getRadius()) { + if (absDistanceB > capsuleB->getHalfHeight()) { float signB = distanceB < 0.0f ? -1.0f : 1.0f; distanceB = signB * capsuleB->getHalfHeight(); } From 00544aa97517fe40a7817cdf77cec168d0c5f3b7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 14 Jul 2014 15:18:34 -0700 Subject: [PATCH 07/18] add JointState::_positionInParentFrame --- interface/src/avatar/SkeletonModel.cpp | 4 ++-- interface/src/renderer/JointState.cpp | 13 +++++++++---- interface/src/renderer/JointState.h | 2 ++ interface/src/renderer/Model.cpp | 19 +++++++++++-------- interface/src/renderer/Model.h | 2 +- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index f4f2a33331..1290cfd53f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -589,7 +589,7 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::buildShapes() { - if (!_geometry || _rootIndex == -1) { + if (_geometry == NULL || _jointStates.isEmpty()) { return; } @@ -743,7 +743,7 @@ void SkeletonModel::resetShapePositionsToDefaultPose() { // Moves shapes to the joint default locations for debug visibility into // how the bounding shape is computed. - if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { + if (!_geometry || _shapes.isEmpty()) { // geometry or joints have not yet been created return; } diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 7958e8b6cd..22473d4d39 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -76,17 +76,22 @@ void JointState::copyState(const JointState& state) { // DO NOT copy _fbxJoint or _constraint } +void JointState::initTransform(const glm::mat4& parentTransform) { + computeTransform(parentTransform); + _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); +} + void JointState::computeTransform(const glm::mat4& parentTransform) { glm::quat rotationInConstrainedFrame = _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; - _transform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform; + glm::mat4 rotationInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; + _transform = parentTransform * glm::translate(_fbxJoint->translation) * rotationInParentFrame; _rotation = extractRotation(_transform); } void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { glm::quat rotationInConstrainedFrame = _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; - _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform; + glm::mat4 rotationInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; + _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * rotationInParentFrame; _visibleRotation = extractRotation(_visibleTransform); } diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 36a0212b45..c0b08d2cff 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -32,6 +32,7 @@ public: void updateConstraint(); void copyState(const JointState& state); + void initTransform(const glm::mat4& parentTransform); void computeTransform(const glm::mat4& parentTransform); void computeVisibleTransform(const glm::mat4& parentTransform); @@ -98,6 +99,7 @@ private: glm::mat4 _transform; // joint- to model-frame glm::quat _rotation; // joint- to model-frame glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied + glm::vec3 _positionInParentFrame; // only changes when the Model is scaled glm::mat4 _visibleTransform; glm::quat _visibleRotation; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5fec542c6e..6394cd8c29 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -40,7 +40,6 @@ Model::Model(QObject* parent) : _snapModelToCenter(false), _snappedToCenter(false), _showTrueJointTransforms(true), - _rootIndex(-1), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com") { @@ -126,6 +125,7 @@ void Model::setScaleInternal(const glm::vec3& scale) { const float ONE_PERCENT = 0.01f; if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; + initJointTransforms(); if (_shapes.size() > 0) { clearShapes(); buildShapes(); @@ -165,24 +165,26 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { state.setFBXJoint(&joint); jointStates.append(state); } + return jointStates; +}; +void Model::initJointTransforms() { // compute model transforms - int numJoints = jointStates.size(); + int numJoints = _jointStates.size(); for (int i = 0; i < numJoints; ++i) { - JointState& state = jointStates[i]; + JointState& state = _jointStates[i]; const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { - _rootIndex = i; + const FBXGeometry& geometry = _geometry->getFBXGeometry(); // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - state.computeTransform(parentTransform); + state.initTransform(parentTransform); } else { - const JointState& parentState = jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform()); + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); } } - return jointStates; } void Model::init() { @@ -560,6 +562,7 @@ bool Model::updateGeometry() { // virtual void Model::setJointStates(QVector states) { _jointStates = states; + initJointTransforms(); int numJoints = _jointStates.size(); float radius = 0.0f; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index dde3a68218..078685345b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -164,7 +164,6 @@ protected: bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snappedToCenter; /// are we currently snapped to center bool _showTrueJointTransforms; - int _rootIndex; glm::vec3 _localLightDirections[MAX_LOCAL_LIGHTS]; glm::vec3 _localLightColors[MAX_LOCAL_LIGHTS]; @@ -227,6 +226,7 @@ private: void deleteGeometry(); void renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows); QVector createJointStates(const FBXGeometry& geometry); + void initJointTransforms(); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base QSharedPointer _nextBaseGeometry; From b8fb5e0298ddc94ce3956ff37459144bd9c97f8c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 14 Jul 2014 15:33:05 -0700 Subject: [PATCH 08/18] namechange FromBindFrame ==> InBindFrame --- interface/src/avatar/SkeletonModel.cpp | 10 +++++----- interface/src/renderer/JointState.cpp | 4 ++-- interface/src/renderer/JointState.h | 4 ++-- interface/src/renderer/Model.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1290cfd53f..e975fc2d78 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -69,7 +69,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex); if (jointIndex != -1) { JointState& state = _jointStates[jointIndex]; - state.setRotationFromBindFrame(prioVR->getJointRotations().at(i), PALM_PRIORITY); + state.setRotationInBindFrame(prioVR->getJointRotations().at(i), PALM_PRIORITY); } } return; @@ -217,7 +217,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { setJointPosition(parentJointIndex, palmPosition + forearm, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); JointState& parentState = _jointStates[parentJointIndex]; - parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY); + parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity _jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat()); } else { @@ -381,13 +381,13 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); JointState& shoulderState = _jointStates[shoulderJointIndex]; - shoulderState.setRotationFromBindFrame(shoulderRotation, PALM_PRIORITY); + shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY); JointState& elbowState = _jointStates[elbowJointIndex]; - elbowState.setRotationFromBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY); + elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY); JointState& handState = _jointStates[jointIndex]; - handState.setRotationFromBindFrame(rotation, PALM_PRIORITY); + handState.setRotationInBindFrame(rotation, PALM_PRIORITY); } bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 22473d4d39..bcf7d724ba 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -95,7 +95,7 @@ void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { _visibleRotation = extractRotation(_visibleTransform); } -glm::quat JointState::getRotationFromBindToModelFrame() const { +glm::quat JointState::getRotationInBindFrame() const { return _rotation * _fbxJoint->inverseBindRotation; } @@ -107,7 +107,7 @@ void JointState::restoreRotation(float fraction, float priority) { } } -void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain) { +void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain) { // rotation is from bind- to model-frame assert(_fbxJoint != NULL); if (priority >= _animationPriority) { diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index c0b08d2cff..99cf14bf3a 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -46,7 +46,7 @@ public: glm::vec3 getPosition() const { return extractTranslation(_transform); } /// \return rotation from bind to model frame - glm::quat getRotationFromBindToModelFrame() const; + glm::quat getRotationInBindFrame() const; int getParentIndex() const { return _fbxJoint->parentIndex; } @@ -72,7 +72,7 @@ public: /// \param rotation is from bind- to model-frame /// computes and sets new _rotationInConstrainedFrame /// NOTE: the JointState's model-frame transform/rotation are NOT updated! - void setRotationFromBindFrame(const glm::quat& rotation, float priority, bool constrain = false); + void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false); void setRotationInConstrainedFrame(const glm::quat& targetRotation); void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 6394cd8c29..d65cb014c9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1041,8 +1041,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl if (useRotation) { JointState& state = _jointStates[jointIndex]; - state.setRotationFromBindFrame(rotation, priority); - endRotation = state.getRotationFromBindToModelFrame(); + state.setRotationInBindFrame(rotation, priority); + endRotation = state.getRotationInBindFrame(); } // then, we go from the joint upwards, rotating the end as close as possible to the target @@ -1213,7 +1213,7 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm: } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); // set final rotation of the end joint - endState.setRotationFromBindFrame(targetRotation, priority, true); + endState.setRotationInBindFrame(targetRotation, priority, true); _shapesAreDirty = !_shapes.isEmpty(); } From ecc83e9559322790143cfcc1f2adc92fde88aa3c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 14 Jul 2014 16:01:25 -0700 Subject: [PATCH 09/18] add getRotationInParentFrame() --- interface/src/renderer/JointState.cpp | 20 ++++++++++++++------ interface/src/renderer/JointState.h | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index bcf7d724ba..01b81f6e56 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -82,16 +82,16 @@ void JointState::initTransform(const glm::mat4& parentTransform) { } void JointState::computeTransform(const glm::mat4& parentTransform) { - glm::quat rotationInConstrainedFrame = _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 rotationInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; - _transform = parentTransform * glm::translate(_fbxJoint->translation) * rotationInParentFrame; + glm::quat rotationInParentFrame = _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; + glm::mat4 transformInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInParentFrame) * _fbxJoint->postTransform; + _transform = parentTransform * glm::translate(_fbxJoint->translation) * transformInParentFrame; _rotation = extractRotation(_transform); } void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { - glm::quat rotationInConstrainedFrame = _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; - glm::mat4 rotationInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInConstrainedFrame) * _fbxJoint->postTransform; - _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * rotationInParentFrame; + glm::quat rotationInParentFrame = _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; + glm::mat4 transformInParentFrame = _fbxJoint->preTransform * glm::mat4_cast(rotationInParentFrame) * _fbxJoint->postTransform; + _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * transformInParentFrame; _visibleRotation = extractRotation(_visibleTransform); } @@ -99,6 +99,14 @@ glm::quat JointState::getRotationInBindFrame() const { return _rotation * _fbxJoint->inverseBindRotation; } +glm::quat JointState::getRotationInParentFrame() const { + return _fbxJoint->preRotation * _rotationInConstrainedFrame * _fbxJoint->postRotation; +} + +glm::quat JointState::getVisibleRotationInParentFrame() const { + return _fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation; +} + void JointState::restoreRotation(float fraction, float priority) { assert(_fbxJoint != NULL); if (priority == _animationPriority || _animationPriority == 0.0f) { diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 99cf14bf3a..b4c3633dfa 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -48,6 +48,9 @@ public: /// \return rotation from bind to model frame glm::quat getRotationInBindFrame() const; + glm::quat getRotationInParentFrame() const; + glm::quat getVisibleRotationInParentFrame() const; + int getParentIndex() const { return _fbxJoint->parentIndex; } /// \param rotation rotation of joint in model-frame From 509445d1a2f2df6502c8e5db75e5c4fae1b0e928 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Jul 2014 08:52:21 -0700 Subject: [PATCH 10/18] add JointState::getPositionInParentFrame() --- interface/src/renderer/JointState.cpp | 3 +++ interface/src/renderer/JointState.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 01b81f6e56..acf096c71a 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -19,6 +19,7 @@ JointState::JointState() : _animationPriority(0.0f), + _positionInParentFrame(0.0f), _fbxJoint(NULL), _constraint(NULL) { } @@ -27,6 +28,7 @@ JointState::JointState(const JointState& other) : _constraint(NULL) { _transform = other._transform; _rotation = other._rotation; _rotationInConstrainedFrame = other._rotationInConstrainedFrame; + _positionInParentFrame = other._positionInParentFrame; _animationPriority = other._animationPriority; _fbxJoint = other._fbxJoint; // DO NOT copy _constraint @@ -69,6 +71,7 @@ void JointState::copyState(const JointState& state) { _transform = state._transform; _rotation = extractRotation(_transform); _rotationInConstrainedFrame = state._rotationInConstrainedFrame; + _positionInParentFrame = state._positionInParentFrame; _visibleTransform = state._visibleTransform; _visibleRotation = extractRotation(_visibleTransform); diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index b4c3633dfa..86ee5d01b7 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -50,6 +50,7 @@ public: glm::quat getRotationInParentFrame() const; glm::quat getVisibleRotationInParentFrame() const; + const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; } int getParentIndex() const { return _fbxJoint->parentIndex; } @@ -90,12 +91,12 @@ public: float _animationPriority; // the priority of the animation affecting this joint -private: /// \return parent model-frame rotation // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) glm::quat computeParentRotation() const; glm::quat computeVisibleParentRotation() const; +private: /// debug helper function void loadBindRotation(); From b59ea6f8b47e867442ffec8703591c061ebe6787 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Jul 2014 08:53:20 -0700 Subject: [PATCH 11/18] whitespace formatting --- interface/src/avatar/MyAvatar.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 746ac72ab2..4e92b39240 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -429,6 +429,7 @@ glm::vec3 MyAvatar::getLeftPalmPosition() { leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation); return leftHandPosition; } + glm::vec3 MyAvatar::getRightPalmPosition() { glm::vec3 rightHandPosition; getSkeletonModel().getRightHandPosition(rightHandPosition); From 0667cb6e5fa13274e1eb1f6d08644ffe9f4301a6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Jul 2014 08:54:28 -0700 Subject: [PATCH 12/18] add constraints between child joints of same parent --- interface/src/avatar/SkeletonModel.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e975fc2d78..b422abbf66 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -522,6 +523,7 @@ void SkeletonModel::buildRagdollConstraints() { const int numPoints = _ragdollPoints.size(); assert(numPoints == _jointStates.size()); + QMultiMap families; for (int i = 0; i < numPoints; ++i) { const JointState& state = _jointStates.at(i); const FBXJoint& joint = state.getFBXJoint(); @@ -532,8 +534,23 @@ void SkeletonModel::buildRagdollConstraints() { } else { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); _ragdollConstraints.push_back(bone); + families.insert(parentIndex, i); } } + // Joints that have multiple children effectively have rigid constraints between the children + // in the parent frame, so we add constraints between children in the same family. + QMultiMap::iterator itr = families.begin(); + while (itr != families.end()) { + QList children = families.values(itr.key()); + if (children.size() > 1) { + for (int i = 1; i < children.size(); ++i) { + DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[children[i-1]]), &(_ragdollPoints[children[i]])); + _ragdollConstraints.push_back(bone); + } + } + ++itr; + } + } void SkeletonModel::updateVisibleJointStates() { @@ -562,6 +579,7 @@ void SkeletonModel::updateVisibleJointStates() { const glm::mat4& parentTransform = parentState.getVisibleTransform(); state.computeVisibleTransform(parentTransform); + // TODO: Andrew to fix instability here // we're looking for the rotation that moves visible bone parallel to ragdoll bone glm::vec3 pivot = extractTranslation(parentTransform); glm::vec3 tip = state.getVisiblePosition(); @@ -645,7 +663,13 @@ void SkeletonModel::buildShapes() { buildRagdollConstraints(); // ... then move shapes back to current joint positions - moveShapesTowardJoints(1.0f); + if (_ragdollPoints.size() == numStates) { + int numJoints = _jointStates.size(); + for (int i = 0; i < numJoints; ++i) { + _ragdollPoints[i]._lastPosition = _ragdollPoints.at(i)._position; + _ragdollPoints[i]._position = _jointStates.at(i).getPosition(); + } + } enforceRagdollConstraints(); } From 3ad4a2e17031a8a659c4b6263f24273c386c53c6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 16 Jul 2014 09:32:46 -0700 Subject: [PATCH 13/18] more stability when fingers follow ragdoll --- interface/src/avatar/SkeletonModel.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b422abbf66..365464d37d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -558,8 +558,11 @@ void SkeletonModel::updateVisibleJointStates() { // no need to update visible transforms return; } + QVector points; + points.reserve(_jointStates.size()); for (int i = 0; i < _jointStates.size(); i++) { JointState& state = _jointStates[i]; + points.push_back(_ragdollPoints[i]._position); // get the parent state (this is the state that we want to rotate) int parentIndex = state.getParentIndex(); @@ -579,12 +582,10 @@ void SkeletonModel::updateVisibleJointStates() { const glm::mat4& parentTransform = parentState.getVisibleTransform(); state.computeVisibleTransform(parentTransform); - // TODO: Andrew to fix instability here // we're looking for the rotation that moves visible bone parallel to ragdoll bone - glm::vec3 pivot = extractTranslation(parentTransform); - glm::vec3 tip = state.getVisiblePosition(); - glm::vec3 shapeTip = _ragdollPoints[i]._position; - glm::quat delta = rotationBetween(tip - pivot, shapeTip - pivot); + // rotationBetween(jointTip - jointPivot, shapeTip - shapePivot) + glm::quat delta = rotationBetween(state.getVisiblePosition() - extractTranslation(parentTransform), + points[i] - points[parentIndex]); // apply parentState.mixVisibleRotationDelta(delta, 0.01f); From 66158cb8a25672db9c97ad6d2d05c001e43ff665 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Jul 2014 09:19:17 -0700 Subject: [PATCH 14/18] fix capsule-vs-capule collision test --- libraries/shared/src/ShapeCollider.cpp | 116 +++++++++++++++++++++---- 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 03c275d987..5e4eff67ec 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -343,6 +343,72 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col return false; } +/// \param lineP point on line +/// \param lineDir normalized direction of line +/// \param cylinderP point on cylinder axis +/// \param cylinderDir normalized direction of cylinder axis +/// \param cylinderRadius radius of cylinder +/// \param hitLow[out] distance from point on line to first intersection with cylinder +/// \param hitHigh[out] distance from point on line to second intersection with cylinder +/// \return true if line hits cylinder +bool lineCylinder(const glm::vec3& lineP, const glm::vec3& lineDir, + const glm::vec3& cylinderP, const glm::vec3& cylinderDir, float cylinderRadius, + float& hitLow, float& hitHigh) { + + // first handle parallel case + float uDotV = glm::dot(lineDir, cylinderDir); + if (fabsf(1.0f - fabsf(uDotV)) < EPSILON) { + // line and cylinder are parallel + if (glm::distance2(lineP, cylinderP) <= cylinderRadius * cylinderRadius) { + // line is inside cylinder, which we consider a hit + hitLow = 0.0f; + hitHigh = 0.0f; + return true; + } + return false; + } + + // Given a line with point 'p' and normalized direction 'u' and + // a cylinder with axial point 's', radius 'r', and normalized direction 'v' + // the intersection of the two is on the line at distance 't' from 'p'. + // + // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 + // + // where: + // + // P = p-s + // w = u-(u.v)v + // Q = P-(P.v)v + // + // A = w^2 + // B = 2(w.Q) + // C = Q^2 - r^2 + + glm::vec3 P = lineP - cylinderP; + glm::vec3 w = lineDir - uDotV * cylinderDir; + glm::vec3 Q = P - glm::dot(P, cylinderDir) * cylinderDir; + + // we save a few multiplies by storing 2*A rather than just A + float A2 = 2.0f * glm::dot(w, w); + float B = 2.0f * glm::dot(w, Q); + + // since C is only ever used once (in the determinant) we compute it inline + float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - cylinderRadius * cylinderRadius); + if (determinant < 0.0f) { + return false; + } + hitLow = (-B - sqrtf(determinant)) / A2; + hitHigh = -(hitLow + 2.0f * B / A2); + + if (hitLow > hitHigh) { + // re-arrange so hitLow is always the smaller value + float temp = hitHigh; + hitHigh = hitLow; + hitLow = temp; + } + return true; +} + bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions) { glm::vec3 axisA; capsuleA->computeNormalizedAxis(axisA); @@ -358,23 +424,43 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, float denominator = 1.0f - aDotB * aDotB; float totalRadius = capsuleA->getRadius() + capsuleB->getRadius(); if (denominator > EPSILON) { - // distances to points of closest approach - float distanceA = glm::dot((centerB - centerA), (axisA - (aDotB) * axisB)) / denominator; - float distanceB = glm::dot((centerA - centerB), (axisB - (aDotB) * axisA)) / denominator; - - // clamp the distances to the ends of the capsule line segments - float absDistanceA = fabs(distanceA); - if (absDistanceA > capsuleA->getHalfHeight()) { - float signA = distanceA < 0.0f ? -1.0f : 1.0f; - distanceA = signA * capsuleA->getHalfHeight(); - } - float absDistanceB = fabs(distanceB); - if (absDistanceB > capsuleB->getHalfHeight()) { - float signB = distanceB < 0.0f ? -1.0f : 1.0f; - distanceB = signB * capsuleB->getHalfHeight(); + // perform line-cylinder intesection test between axis of cylinderA and cylinderB with exanded radius + float hitLow = 0.0f; + float hitHigh = 0.0f; + if (!lineCylinder(centerA, axisA, centerB, axisB, totalRadius, hitLow, hitHigh)) { + return false; } - // collide like spheres at closest approaches (do most of the math relative to B) + float halfHeightA = capsuleA->getHalfHeight(); + if (hitLow > halfHeightA || hitHigh < -halfHeightA) { + // the intersections are off the ends of capsuleA + return false; + } + + // compute nearest approach on axisA of axisB + float distanceA = glm::dot((centerB - centerA), (axisA - (aDotB) * axisB)) / denominator; + // clamp to intersection zone + if (distanceA > hitLow) { + if (distanceA > hitHigh) { + distanceA = hitHigh; + } + } else { + distanceA = hitLow; + } + // clamp to capsule segment + distanceA = glm::clamp(distanceA, -halfHeightA, halfHeightA); + + // find the closest point on capsuleB to sphere on capsuleA + float distanceB = glm::dot(centerA + distanceA * axisA - centerB, axisB); + float halfHeightB = capsuleB->getHalfHeight(); + if (fabsf(distanceB) > halfHeightB) { + // we must clamp distanceB... + distanceB = glm::clamp(distanceB, -halfHeightB, halfHeightB); + // ...and therefore must recompute distanceA + distanceA = glm::clamp(glm::dot(centerB + distanceB * axisB - centerA, axisA), -halfHeightA, halfHeightA); + } + + // collide like two spheres (do most of the math relative to B) glm::vec3 BA = (centerB + distanceB * axisB) - (centerA + distanceA * axisA); float distanceSquared = glm::dot(BA, BA); if (distanceSquared < totalRadius * totalRadius) { From 25840cad49cb81beef8ebef2cc93af5d63b2b0df Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Jul 2014 10:33:11 -0700 Subject: [PATCH 15/18] fix bug in mixVisibleRotationDelta() was using a non-visible rotation --- interface/src/renderer/JointState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index acf096c71a..865036df8e 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -183,7 +183,7 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) { // NOTE: delta is in model-frame assert(_fbxJoint != NULL); - glm::quat targetRotation = _visibleRotationInConstrainedFrame * glm::inverse(_rotation) * delta * _rotation; + glm::quat targetRotation = _visibleRotationInConstrainedFrame * glm::inverse(_visibleRotation) * delta * _visibleRotation; if (mixFactor > 0.0f && mixFactor <= 1.0f) { //targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); targetRotation = safeMix(targetRotation, _rotationInConstrainedFrame, mixFactor); From 783cde737645ee5b9dc6b97839e2f7c1332e78a8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Jul 2014 10:51:17 -0700 Subject: [PATCH 16/18] minor tweak to rate at which ragdoll shapes follow joints --- interface/src/avatar/SkeletonModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 365464d37d..59f39834e3 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -597,7 +597,9 @@ void SkeletonModel::updateVisibleJointStates() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { - const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f; + // NOTE: increasing this timescale reduces vibrations in the ragdoll solution and reduces tunneling + // but makes the shapes slower to follow the body (introduces lag). + const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.05f; float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); moveShapesTowardJoints(fraction); } From 53ee5f2340f7b1a636fbf880797f9d5c2f88f747 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Jul 2014 12:42:18 -0700 Subject: [PATCH 17/18] minor cleanup around measurements of joint radius --- libraries/fbx/src/FBXReader.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 56fb566d6a..0e1b1ae0c6 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -967,14 +967,16 @@ QString getString(const QVariant& value) { class JointShapeInfo { public: - JointShapeInfo() : numVertices(0), numProjectedVertices(0), averageVertex(0.f), boneBegin(0.f), averageRadius(0.f) { - extents.reset(); + JointShapeInfo() : numVertices(0), numProjectedVertices(0), + sumVertexWeights(0.0f), sumWeightedRadii(0.0f), + averageVertex(0.f), boneBegin(0.f), averageRadius(0.f) { } // NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin int numVertices; // num vertices from contributing meshes int numProjectedVertices; // num vertices that successfully project onto bone axis - Extents extents; // max and min extents of mesh vertices (in joint frame) + float sumVertexWeights; + float sumWeightedRadii; glm::vec3 averageVertex; // average of all mesh vertices (in joint frame) glm::vec3 boneBegin; // parent joint location (in joint frame) float averageRadius; // average distance from mesh points to averageVertex @@ -1740,14 +1742,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; if (weight > EXPANSION_WEIGHT_THRESHOLD) { const glm::vec3& vertex = extracted.mesh.vertices.at(it.value()); - float proj = glm::dot(boneDirection, vertex - boneEnd); - if (proj < 0.0f && proj > -boneLength) { + float proj = glm::dot(boneDirection, boneEnd - vertex); + if (proj > 0.0f && proj < boneLength) { joint.boneRadius = glm::max(joint.boneRadius, - radiusScale * glm::distance(vertex, boneEnd + boneDirection * proj)); + radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj)); ++jointShapeInfo.numProjectedVertices; } glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.extents.addPoint(vertexInJointFrame); jointShapeInfo.averageVertex += vertexInJointFrame; ++jointShapeInfo.numVertices; } @@ -1792,13 +1793,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 averageVertex(0.f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { - float proj = glm::dot(boneDirection, vertex - boneEnd); - if (proj < 0.0f && proj > -boneLength) { - joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(vertex, boneEnd + boneDirection * proj)); + float proj = glm::dot(boneDirection, boneEnd - vertex); + if (proj > 0.0f && proj < boneLength) { + joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj)); ++jointShapeInfo.numProjectedVertices; } glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); - jointShapeInfo.extents.addPoint(vertexInJointFrame); jointShapeInfo.averageVertex += vertexInJointFrame; averageVertex += vertex; } From 58e31abf60bc1eb022bf1c83de428df064faeae4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Jul 2014 14:31:16 -0700 Subject: [PATCH 18/18] improved collision shapes for fingers --- libraries/fbx/src/FBXReader.cpp | 49 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 0e1b1ae0c6..594ad7ebe5 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -967,19 +967,19 @@ QString getString(const QVariant& value) { class JointShapeInfo { public: - JointShapeInfo() : numVertices(0), numProjectedVertices(0), - sumVertexWeights(0.0f), sumWeightedRadii(0.0f), + JointShapeInfo() : numVertices(0), + sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0), averageVertex(0.f), boneBegin(0.f), averageRadius(0.f) { } // NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin - int numVertices; // num vertices from contributing meshes - int numProjectedVertices; // num vertices that successfully project onto bone axis - float sumVertexWeights; - float sumWeightedRadii; - glm::vec3 averageVertex; // average of all mesh vertices (in joint frame) - glm::vec3 boneBegin; // parent joint location (in joint frame) - float averageRadius; // average distance from mesh points to averageVertex + int numVertices; // num vertices from contributing meshes + float sumVertexWeights; // sum of all vertex weights + float sumWeightedRadii; // sum of weighted vertices + int numVertexWeights; // num vertices that contributed to sums + glm::vec3 averageVertex;// average of all mesh vertices (in joint frame) + glm::vec3 boneBegin; // parent joint location (in joint frame) + float averageRadius; // average distance from mesh points to averageVertex }; class AnimationCurve { @@ -1743,11 +1743,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (weight > EXPANSION_WEIGHT_THRESHOLD) { const glm::vec3& vertex = extracted.mesh.vertices.at(it.value()); float proj = glm::dot(boneDirection, boneEnd - vertex); - if (proj > 0.0f && proj < boneLength) { - joint.boneRadius = glm::max(joint.boneRadius, - radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj)); - ++jointShapeInfo.numProjectedVertices; + if (proj < 0.0f || proj > boneLength) { + weight *= 0.5f; } + + jointShapeInfo.sumVertexWeights += weight; + jointShapeInfo.sumWeightedRadii += weight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); + ++jointShapeInfo.numVertexWeights; + glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); jointShapeInfo.averageVertex += vertexInJointFrame; ++jointShapeInfo.numVertices; @@ -1793,11 +1796,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 averageVertex(0.f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { + float weight = 1.0f; float proj = glm::dot(boneDirection, boneEnd - vertex); - if (proj > 0.0f && proj < boneLength) { - joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj)); - ++jointShapeInfo.numProjectedVertices; + if (proj < 0.0f || proj > boneLength) { + weight *= 0.5f; } + jointShapeInfo.sumVertexWeights += weight; + jointShapeInfo.sumWeightedRadii += weight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); + ++jointShapeInfo.numVertexWeights; + glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); jointShapeInfo.averageVertex += vertexInJointFrame; averageVertex += vertex; @@ -1832,9 +1839,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) jointShapeInfo.boneBegin = inverseRotation * (extractTranslation(parentJoint.transform) - extractTranslation(joint.transform)); } - // we use a capsule if the joint ANY mesh vertices successfully projected onto the bone + if (jointShapeInfo.sumVertexWeights > 0.0f) { + joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights; + } + + // we use a capsule if the joint had ANY mesh vertices successfully projected onto the bone // AND its boneRadius is not too close to zero - bool collideLikeCapsule = jointShapeInfo.numProjectedVertices > 0 + bool collideLikeCapsule = jointShapeInfo.numVertexWeights > 0 && glm::length(jointShapeInfo.boneBegin) > EPSILON; if (collideLikeCapsule) { @@ -1850,7 +1861,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else { joint.shapePosition = glm::vec3(0.f); } - if (jointShapeInfo.numProjectedVertices == 0 + if (jointShapeInfo.numVertexWeights == 0 && jointShapeInfo.numVertices > 0) { // the bone projection algorithm was not able to compute the joint radius // so we use an alternative measure