Showing the changes to interface/src/avatar/SkeletonModel.cpp (1 of 10 files). Show all
Unified · Split
| @@ -11,8 +11,8 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include <glm/gtx/transform.hpp> |
| 13 | 13 | |
| 14 | −#include <CapsuleShape.h> | |
| 15 | −#include <SphereShape.h> | |
| 14 | +#include <VerletCapsuleShape.h> | |
| 15 | +#include <VerletSphereShape.h> | |
| 16 | 16 | |
| 17 | 17 | #include "Application.h" |
| 18 | 18 | #include "Avatar.h" |
| @@ -31,20 +31,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : · 12 unmodified lines | ||
| 31 | 31 | void SkeletonModel::setJointStates(QVector<JointState> states) { |
| 32 | 32 | Model::setJointStates(states); |
| 33 | 33 | |
| 34 | − if (isActive() && _owningAvatar->isMyAvatar()) { | |
| 35 | − // extract lists of parentIndex and position from _jointStates... | |
| 36 | − QVector<int> parentIndices; | |
| 37 | − QVector<glm::vec3> points; | |
| 38 | − int numJoints = _jointStates.size(); | |
| 39 | − parentIndices.reserve(numJoints); | |
| 40 | − points.reserve(numJoints); | |
| 41 | − for (int i = 0; i < numJoints; ++i) { | |
| 42 | − JointState& state = _jointStates[i]; | |
| 43 | − parentIndices.push_back(state.getFBXJoint().parentIndex); | |
| 44 | − points.push_back(state.getPosition()); | |
| 45 | − } | |
| 46 | − // ... and feed the results to Ragdoll | |
| 47 | − initShapesAndPoints(&_shapes, parentIndices, points); | |
| 34 | + clearShapes(); | |
| 35 | + clearRagdollConstraintsAndPoints(); | |
| 36 | + if (_enableShapes) { | |
| 37 | + buildShapes(); | |
| 48 | 38 | } |
| 49 | 39 | } |
| 50 | 40 | |
| @@ -110,14 +100,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 59 unmodified lines | ||
| 110 | 100 | } |
| 111 | 101 | |
| 112 | 102 | void SkeletonModel::simulateRagdoll(float deltaTime) { |
| 113 | − // move ragdoll _points toward joints | |
| 103 | + // move _ragdollPoints toward joints | |
| 114 | 104 | const int numStates = _jointStates.size(); |
| 115 | − assert(numStates == _points.size()); | |
| 105 | + assert(numStates == _ragdollPoints.size()); | |
| 116 | 106 | float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes |
| 117 | − float oneMinusFraction = 1.0f - fraction; | |
| 118 | − for (int i = 0; i < numStates; ++i) { | |
| 119 | − _points[i] = oneMinusFraction * _points[i] + fraction * _jointStates[i].getPosition(); | |
| 120 | − } | |
| 107 | + moveShapesTowardJoints(fraction); | |
| 121 | 108 | |
| 122 | 109 | // enforce the constraints |
| 123 | 110 | float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm |
| @@ -125,7 +112,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) { · 1 unmodified line | ||
| 125 | 112 | int iterations = 0; |
| 126 | 113 | float delta = 0.0f; |
| 127 | 114 | do { |
| 128 | − delta = enforceConstraints(); | |
| 115 | + delta = enforceRagdollConstraints(); | |
| 129 | 116 | ++iterations; |
| 130 | 117 | } while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS); |
| 131 | 118 | } |
| @@ -274,16 +261,6 @@ void SkeletonModel::updateJointState(int index) { · 142 unmodified lines | ||
| 274 | 261 | } |
| 275 | 262 | } |
| 276 | 263 | |
| 277 | −void SkeletonModel::updateShapePositions() { | |
| 278 | − if (isActive() && _owningAvatar->isMyAvatar() && | |
| 279 | − Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { | |
| 280 | − // TODO: Andrew to move shape updates to SimulationEngine | |
| 281 | − //updateShapes(_rotation, _translation); | |
| 282 | − } else { | |
| 283 | − Model::updateShapePositions(); | |
| 284 | − } | |
| 285 | −} | |
| 286 | − | |
| 287 | 264 | void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { |
| 288 | 265 | if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { |
| 289 | 266 | return; |
| @@ -516,14 +493,14 @@ void SkeletonModel::renderRagdoll() { · 226 unmodified lines | ||
| 516 | 493 | glPushMatrix(); |
| 517 | 494 | |
| 518 | 495 | Application::getInstance()->loadTranslatedViewMatrix(_translation); |
| 519 | − int numPoints = _points.size(); | |
| 496 | + int numPoints = _ragdollPoints.size(); | |
| 520 | 497 | float alpha = 0.3f; |
| 521 | 498 | float radius1 = 0.008f; |
| 522 | 499 | float radius2 = 0.01f; |
| 523 | 500 | for (int i = 0; i < numPoints; ++i) { |
| 524 | 501 | glPushMatrix(); |
| 525 | 502 | // draw each point as a yellow hexagon with black border |
| 526 | − glm::vec3 position = _rotation * _points[i]; | |
| 503 | + glm::vec3 position = _rotation * _ragdollPoints[i]; | |
| 527 | 504 | glTranslatef(position.x, position.y, position.z); |
| 528 | 505 | glColor4f(0.0f, 0.0f, 0.0f, alpha); |
| 529 | 506 | glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); |
| @@ -536,6 +513,22 @@ void SkeletonModel::renderRagdoll() { · 6 unmodified lines | ||
| 536 | 513 | glEnable(GL_LIGHTING); |
| 537 | 514 | } |
| 538 | 515 | |
| 516 | +// virtual | |
| 517 | +void SkeletonModel::initRagdollPoints() { | |
| 518 | + assert(_ragdollPoints.size() == 0); | |
| 519 | + // one point for each joint | |
| 520 | + int numJoints = _jointStates.size(); | |
| 521 | + _ragdollPoints.reserve(numJoints); | |
| 522 | + for (int i = 0; i < numJoints; ++i) { | |
| 523 | + const JointState& state = _jointStates.at(i); | |
| 524 | + _ragdollPoints.push_back(state.getPosition()); | |
| 525 | + } | |
| 526 | +} | |
| 527 | + | |
| 528 | +// virtual | |
| 529 | +void SkeletonModel::stepRagdollForward(float deltaTime) { | |
| 530 | +} | |
| 531 | + | |
| 539 | 532 | // virtual |
| 540 | 533 | void SkeletonModel::buildShapes() { |
| 541 | 534 | if (!_geometry || _rootIndex == -1) { |
| @@ -546,42 +539,53 @@ void SkeletonModel::buildShapes() { · 4 unmodified lines | ||
| 546 | 539 | if (geometry.joints.isEmpty()) { |
| 547 | 540 | return; |
| 548 | 541 | } |
| 549 | − // We create the shapes with proper dimensions, but we set their transforms later. | |
| 542 | + | |
| 543 | + initRagdollPoints(); | |
| 544 | + | |
| 550 | 545 | float uniformScale = extractUniformScale(_scale); |
| 551 | − for (int i = 0; i < _jointStates.size(); i++) { | |
| 546 | + const int numStates = _jointStates.size(); | |
| 547 | + for (int i = 0; i < numStates; i++) { | |
| 552 | 548 | const FBXJoint& joint = geometry.joints[i]; |
| 553 | − | |
| 554 | 549 | float radius = uniformScale * joint.boneRadius; |
| 555 | 550 | float halfHeight = 0.5f * uniformScale * joint.distanceToParent; |
| 556 | 551 | Shape::Type type = joint.shapeType; |
| 557 | − if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { | |
| 558 | − // this capsule is effectively a sphere | |
| 552 | + if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) { | |
| 553 | + // this shape is forced to be a sphere | |
| 559 | 554 | type = Shape::SPHERE_SHAPE; |
| 560 | 555 | } |
| 561 | − if (type == Shape::CAPSULE_SHAPE) { | |
| 562 | − CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); | |
| 563 | − capsule->setEntity(this); | |
| 564 | − _shapes.push_back(capsule); | |
| 565 | − } else if (type == Shape::SPHERE_SHAPE) { | |
| 566 | − SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); | |
| 567 | − _shapes.push_back(sphere); | |
| 568 | − sphere->setEntity(this); | |
| 569 | − } else { | |
| 570 | − // this shape type is not handled and the joint shouldn't collide, | |
| 571 | − // however we must have a Shape* for each joint, so we push NULL | |
| 572 | − _shapes.push_back(NULL); | |
| 573 | − } | |
| 556 | + Shape* shape = NULL; | |
| 557 | + if (type == Shape::SPHERE_SHAPE) { | |
| 558 | + shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); | |
| 559 | + shape->setEntity(this); | |
| 560 | + } else if (type == Shape::CAPSULE_SHAPE) { | |
| 561 | + int parentIndex = joint.parentIndex; | |
| 562 | + assert(parentIndex != -1); | |
| 563 | + shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); | |
| 564 | + shape->setEntity(this); | |
| 565 | + } | |
| 566 | + _shapes.push_back(shape); | |
| 574 | 567 | } |
| 575 | 568 | |
| 576 | 569 | // This method moves the shapes to their default positions in Model frame |
| 577 | 570 | // which is where we compute the bounding shape's parameters. |
| 578 | 571 | computeBoundingShape(geometry); |
| 579 | 572 | |
| 580 | − // finally sync shapes to joint positions | |
| 581 | − _shapesAreDirty = true; | |
| 582 | − updateShapePositions(); | |
| 573 | + // move shapes to joint positions | |
| 574 | + moveShapesTowardJoints(1.0f); | |
| 583 | 575 | } |
| 584 | 576 | |
| 577 | +void SkeletonModel::moveShapesTowardJoints(float fraction) { | |
| 578 | + assert(fraction >= 0.0f && fraction <= 1.0f); | |
| 579 | + if (_ragdollPoints.size() == _jointStates.size()) { | |
| 580 | + float oneMinusFraction = 1.0f - fraction; | |
| 581 | + int numJoints = _jointStates.size(); | |
| 582 | + for (int i = 0; i < numJoints; ++i) { | |
| 583 | + _ragdollPoints[i] = oneMinusFraction * _ragdollPoints[i] + fraction * _jointStates.at(i).getPosition(); | |
| 584 | + } | |
| 585 | + } | |
| 586 | +} | |
| 587 | + | |
| 588 | +/* TODO: Andrew to remove this when done with ragdoll work. | |
| 585 | 589 | void SkeletonModel::updateShapePositionsLegacy() { |
| 586 | 590 | if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { |
| 587 | 591 | glm::vec3 rootPosition(0.0f); |
| @@ -612,65 +616,28 @@ void SkeletonModel::updateShapePositionsLegacy() { · 24 unmodified lines | ||
| 612 | 616 | _boundingShape.setRotation(_rotation); |
| 613 | 617 | } |
| 614 | 618 | } |
| 619 | +*/ | |
| 615 | 620 | |
| 616 | 621 | void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { |
| 617 | − // compute default joint transforms and rotations | |
| 618 | − // (in local frame, ignoring Model translation and rotation) | |
| 622 | + // compute default joint transforms | |
| 619 | 623 | int numJoints = geometry.joints.size(); |
| 624 | + if (numJoints != _ragdollPoints.size()) { | |
| 625 | + return; | |
| 626 | + } | |
| 620 | 627 | QVector<glm::mat4> transforms; |
| 621 | 628 | transforms.fill(glm::mat4(), numJoints); |
| 622 | − QVector<glm::quat> finalRotations; | |
| 623 | − finalRotations.fill(glm::quat(), numJoints); | |
| 624 | − | |
| 625 | − QVector<bool> shapeIsSet; | |
| 626 | − shapeIsSet.fill(false, numJoints); | |
| 627 | − int numShapesSet = 0; | |
| 628 | − int lastNumShapesSet = -1; | |
| 629 | − while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) { | |
| 630 | − lastNumShapesSet = numShapesSet; | |
| 631 | − for (int i = 0; i < numJoints; i++) { | |
| 632 | − const FBXJoint& joint = geometry.joints.at(i); | |
| 633 | − int parentIndex = joint.parentIndex; | |
| 634 | − | |
| 635 | − if (parentIndex == -1) { | |
| 636 | − glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset); | |
| 637 | − glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; | |
| 638 | − glm::mat4 rootTransform = baseTransform * geometry.offset * glm::translate(joint.translation) | |
| 639 | − * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; | |
| 640 | − // remove the tranlsation part before we save the root transform | |
| 641 | − transforms[i] = glm::translate(- extractTranslation(rootTransform)) * rootTransform; | |
| 642 | − | |
| 643 | − finalRotations[i] = combinedRotation; | |
| 644 | − ++numShapesSet; | |
| 645 | − shapeIsSet[i] = true; | |
| 646 | − } else if (shapeIsSet[parentIndex]) { | |
| 647 | − glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; | |
| 648 | − transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) | |
| 649 | − * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; | |
| 650 | − finalRotations[i] = finalRotations[parentIndex] * combinedRotation; | |
| 651 | − ++numShapesSet; | |
| 652 | − shapeIsSet[i] = true; | |
| 653 | − } | |
| 654 | − } | |
| 655 | − } | |
| 656 | 629 | |
| 657 | − // sync shapes to joints | |
| 658 | − _boundingRadius = 0.0f; | |
| 659 | − float uniformScale = extractUniformScale(_scale); | |
| 660 | − for (int i = 0; i < _shapes.size(); i++) { | |
| 661 | − Shape* shape = _shapes[i]; | |
| 662 | − if (!shape) { | |
| 663 | − continue; | |
| 664 | − } | |
| 665 | − const FBXJoint& joint = geometry.joints[i]; | |
| 666 | − glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); | |
| 667 | − glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; | |
| 668 | − shape->setTranslation(localPosition); | |
| 669 | − shape->setRotation(finalRotations[i] * joint.shapeRotation); | |
| 670 | − float distance = glm::length(localPosition) + shape->getBoundingRadius(); | |
| 671 | − if (distance > _boundingRadius) { | |
| 672 | − _boundingRadius = distance; | |
| 673 | − } | |
| 630 | + transforms[0] = glm::scale(_scale); | |
| 631 | + for (int i = 1; i < numJoints; i++) { | |
| 632 | + const FBXJoint& joint = geometry.joints.at(i); | |
| 633 | + int parentIndex = joint.parentIndex; | |
| 634 | + assert(parentIndex != -1); | |
| 635 | + | |
| 636 | + glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; | |
| 637 | + transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) | |
| 638 | + * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; | |
| 639 | + // setting the ragdollPoints here slams the VerletShapes into their default positions | |
| 640 | + _ragdollPoints[i] = extractTranslation(transforms[i]); | |
| 674 | 641 | } |
| 675 | 642 | |
| 676 | 643 | // compute bounding box |
| @@ -682,6 +649,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 5 unmodified lines | ||
| 682 | 649 | if (!shape) { |
| 683 | 650 | continue; |
| 684 | 651 | } |
| 652 | + // TODO: skip hand and arm joints when computing bounding dimensions | |
| 685 | 653 | Extents shapeExtents; |
| 686 | 654 | shapeExtents.reset(); |
| 687 | 655 | glm::vec3 localPosition = shape->getTranslation(); |
| @@ -715,6 +683,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 27 unmodified lines | ||
| 715 | 683 | _boundingShape.setRadius(capsuleRadius); |
| 716 | 684 | _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); |
| 717 | 685 | _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); |
| 686 | + _boundingRadius = 0.5f * glm::length(diagonal); | |
| 718 | 687 | } |
| 719 | 688 | |
| 720 | 689 | void SkeletonModel::resetShapePositions() { |