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 | #include <glm/gtx/transform.hpp> | 12 | #include <glm/gtx/transform.hpp> |
| 13 | 13 | ||
| 14 | #include <CapsuleShape.h> | 14 | #include <VerletCapsuleShape.h> |
| 15 | #include <SphereShape.h> | 15 | #include <VerletSphereShape.h> |
| 16 | 16 | ||
| 17 | #include "Application.h" | 17 | #include "Application.h" |
| 18 | #include "Avatar.h" | 18 | #include "Avatar.h" |
| @@ -31,20 +31,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : · 12 unmodified lines | |||
| 31 | void SkeletonModel::setJointStates(QVector<JointState> states) { | 31 | void SkeletonModel::setJointStates(QVector<JointState> states) { |
| 32 | Model::setJointStates(states); | 32 | Model::setJointStates(states); |
| 33 | 33 | ||
| 34 | if (isActive() && _owningAvatar->isMyAvatar()) { | 34 | clearShapes(); |
| 35 | // extract lists of parentIndex and position from _jointStates... | 35 | clearRagdollConstraintsAndPoints(); |
| 36 | QVector<int> parentIndices; | 36 | if (_enableShapes) { |
| 37 | QVector<glm::vec3> points; | 37 | buildShapes(); |
| 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); | ||
| 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 | void SkeletonModel::simulateRagdoll(float deltaTime) { | 102 | void SkeletonModel::simulateRagdoll(float deltaTime) { |
| 113 | // move ragdoll _points toward joints | 103 | // move _ragdollPoints toward joints |
| 114 | const int numStates = _jointStates.size(); | 104 | const int numStates = _jointStates.size(); |
| 115 | assert(numStates == _points.size()); | 105 | assert(numStates == _ragdollPoints.size()); |
| 116 | float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes | 106 | float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes |
| 117 | float oneMinusFraction = 1.0f - fraction; | 107 | moveShapesTowardJoints(fraction); |
| 118 | for (int i = 0; i < numStates; ++i) { | ||
| 119 | _points[i] = oneMinusFraction * _points[i] + fraction * _jointStates[i].getPosition(); | ||
| 120 | } | ||
| 121 | 108 | ||
| 122 | // enforce the constraints | 109 | // enforce the constraints |
| 123 | float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm | 110 | float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm |
| @@ -125,7 +112,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) { · 1 unmodified line | |||
| 125 | int iterations = 0; | 112 | int iterations = 0; |
| 126 | float delta = 0.0f; | 113 | float delta = 0.0f; |
| 127 | do { | 114 | do { |
| 128 | delta = enforceConstraints(); | 115 | delta = enforceRagdollConstraints(); |
| 129 | ++iterations; | 116 | ++iterations; |
| 130 | } while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS); | 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 | void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { | 264 | void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { |
| 288 | if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { | 265 | if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { |
| 289 | return; | 266 | return; |
| @@ -516,14 +493,14 @@ void SkeletonModel::renderRagdoll() { · 226 unmodified lines | |||
| 516 | glPushMatrix(); | 493 | glPushMatrix(); |
| 517 | 494 | ||
| 518 | Application::getInstance()->loadTranslatedViewMatrix(_translation); | 495 | Application::getInstance()->loadTranslatedViewMatrix(_translation); |
| 519 | int numPoints = _points.size(); | 496 | int numPoints = _ragdollPoints.size(); |
| 520 | float alpha = 0.3f; | 497 | float alpha = 0.3f; |
| 521 | float radius1 = 0.008f; | 498 | float radius1 = 0.008f; |
| 522 | float radius2 = 0.01f; | 499 | float radius2 = 0.01f; |
| 523 | for (int i = 0; i < numPoints; ++i) { | 500 | for (int i = 0; i < numPoints; ++i) { |
| 524 | glPushMatrix(); | 501 | glPushMatrix(); |
| 525 | // draw each point as a yellow hexagon with black border | 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 | glTranslatef(position.x, position.y, position.z); | 504 | glTranslatef(position.x, position.y, position.z); |
| 528 | glColor4f(0.0f, 0.0f, 0.0f, alpha); | 505 | glColor4f(0.0f, 0.0f, 0.0f, alpha); |
| 529 | glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); | 506 | glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); |
| @@ -536,6 +513,22 @@ void SkeletonModel::renderRagdoll() { · 6 unmodified lines | |||
| 536 | glEnable(GL_LIGHTING); | 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 | // virtual | 532 | // virtual |
| 540 | void SkeletonModel::buildShapes() { | 533 | void SkeletonModel::buildShapes() { |
| 541 | if (!_geometry || _rootIndex == -1) { | 534 | if (!_geometry || _rootIndex == -1) { |
| @@ -546,42 +539,53 @@ void SkeletonModel::buildShapes() { · 4 unmodified lines | |||
| 546 | if (geometry.joints.isEmpty()) { | 539 | if (geometry.joints.isEmpty()) { |
| 547 | return; | 540 | return; |
| 548 | } | 541 | } |
| 549 | // We create the shapes with proper dimensions, but we set their transforms later. | 542 | |
| 543 | initRagdollPoints(); | ||
| 544 | |||
| 550 | float uniformScale = extractUniformScale(_scale); | 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 | const FBXJoint& joint = geometry.joints[i]; | 548 | const FBXJoint& joint = geometry.joints[i]; |
| 553 | |||
| 554 | float radius = uniformScale * joint.boneRadius; | 549 | float radius = uniformScale * joint.boneRadius; |
| 555 | float halfHeight = 0.5f * uniformScale * joint.distanceToParent; | 550 | float halfHeight = 0.5f * uniformScale * joint.distanceToParent; |
| 556 | Shape::Type type = joint.shapeType; | 551 | Shape::Type type = joint.shapeType; |
| 557 | if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { | 552 | if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) { |
| 558 | // this capsule is effectively a sphere | 553 | // this shape is forced to be a sphere |
| 559 | type = Shape::SPHERE_SHAPE; | 554 | type = Shape::SPHERE_SHAPE; |
| 560 | } | 555 | } |
| 561 | if (type == Shape::CAPSULE_SHAPE) { | 556 | Shape* shape = NULL; |
| 562 | CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); | 557 | if (type == Shape::SPHERE_SHAPE) { |
| 563 | capsule->setEntity(this); | 558 | shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); |
| 564 | _shapes.push_back(capsule); | 559 | shape->setEntity(this); |
| 565 | } else if (type == Shape::SPHERE_SHAPE) { | 560 | } else if (type == Shape::CAPSULE_SHAPE) { |
| 566 | SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); | 561 | int parentIndex = joint.parentIndex; |
| 567 | _shapes.push_back(sphere); | 562 | assert(parentIndex != -1); |
| 568 | sphere->setEntity(this); | 563 | shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); |
| 569 | } else { | 564 | shape->setEntity(this); |
| 570 | // this shape type is not handled and the joint shouldn't collide, | 565 | } |
| 571 | // however we must have a Shape* for each joint, so we push NULL | 566 | _shapes.push_back(shape); |
| 572 | _shapes.push_back(NULL); | ||
| 573 | } | ||
| 574 | } | 567 | } |
| 575 | 568 | ||
| 576 | // This method moves the shapes to their default positions in Model frame | 569 | // This method moves the shapes to their default positions in Model frame |
| 577 | // which is where we compute the bounding shape's parameters. | 570 | // which is where we compute the bounding shape's parameters. |
| 578 | computeBoundingShape(geometry); | 571 | computeBoundingShape(geometry); |
| 579 | 572 | ||
| 580 | // finally sync shapes to joint positions | 573 | // move shapes to joint positions |
| 581 | _shapesAreDirty = true; | 574 | moveShapesTowardJoints(1.0f); |
| 582 | updateShapePositions(); | ||
| 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 | void SkeletonModel::updateShapePositionsLegacy() { | 589 | void SkeletonModel::updateShapePositionsLegacy() { |
| 586 | if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { | 590 | if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { |
| 587 | glm::vec3 rootPosition(0.0f); | 591 | glm::vec3 rootPosition(0.0f); |
| @@ -612,65 +616,28 @@ void SkeletonModel::updateShapePositionsLegacy() { · 24 unmodified lines | |||
| 612 | _boundingShape.setRotation(_rotation); | 616 | _boundingShape.setRotation(_rotation); |
| 613 | } | 617 | } |
| 614 | } | 618 | } |
| 619 | */ | ||
| 615 | 620 | ||
| 616 | void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { | 621 | void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { |
| 617 | // compute default joint transforms and rotations | 622 | // compute default joint transforms |
| 618 | // (in local frame, ignoring Model translation and rotation) | ||
| 619 | int numJoints = geometry.joints.size(); | 623 | int numJoints = geometry.joints.size(); |
| 624 | if (numJoints != _ragdollPoints.size()) { | ||
| 625 | return; | ||
| 626 | } | ||
| 620 | QVector<glm::mat4> transforms; | 627 | QVector<glm::mat4> transforms; |
| 621 | transforms.fill(glm::mat4(), numJoints); | 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 | 630 | transforms[0] = glm::scale(_scale); |
| 658 | _boundingRadius = 0.0f; | 631 | for (int i = 1; i < numJoints; i++) { |
| 659 | float uniformScale = extractUniformScale(_scale); | 632 | const FBXJoint& joint = geometry.joints.at(i); |
| 660 | for (int i = 0; i < _shapes.size(); i++) { | 633 | int parentIndex = joint.parentIndex; |
| 661 | Shape* shape = _shapes[i]; | 634 | assert(parentIndex != -1); |
| 662 | if (!shape) { | 635 | |
| 663 | continue; | 636 | glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; |
| 664 | } | 637 | transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) |
| 665 | const FBXJoint& joint = geometry.joints[i]; | 638 | * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; |
| 666 | glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); | 639 | // setting the ragdollPoints here slams the VerletShapes into their default positions |
| 667 | glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; | 640 | _ragdollPoints[i] = extractTranslation(transforms[i]); |
| 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 | } | ||
| 674 | } | 641 | } |
| 675 | 642 | ||
| 676 | // compute bounding box | 643 | // compute bounding box |
| @@ -682,6 +649,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 5 unmodified lines | |||
| 682 | if (!shape) { | 649 | if (!shape) { |
| 683 | continue; | 650 | continue; |
| 684 | } | 651 | } |
| 652 | // TODO: skip hand and arm joints when computing bounding dimensions | ||
| 685 | Extents shapeExtents; | 653 | Extents shapeExtents; |
| 686 | shapeExtents.reset(); | 654 | shapeExtents.reset(); |
| 687 | glm::vec3 localPosition = shape->getTranslation(); | 655 | glm::vec3 localPosition = shape->getTranslation(); |
| @@ -715,6 +683,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { · 27 unmodified lines | |||
| 715 | _boundingShape.setRadius(capsuleRadius); | 683 | _boundingShape.setRadius(capsuleRadius); |
| 716 | _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); | 684 | _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); |
| 717 | _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); | 685 | _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); |
| 686 | _boundingRadius = 0.5f * glm::length(diagonal); | ||
| 718 | } | 687 | } |
| 719 | 688 | ||
| 720 | void SkeletonModel::resetShapePositions() { | 689 | void SkeletonModel::resetShapePositions() { |