void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms int numStates = _jointStates.size(); assert(numStates == _shapes.size()); QVector<glm::mat4> transforms; transforms.fill(glm::mat4(), numStates); // compute bounding box that encloses all shapes Extents totalExtents; totalExtents.reset(); totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < numStates; i++) { // compute the default transform of this joint JointState& state = _jointStates[i]; const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { transforms[i] = _jointStates[i].getTransform(); } else { glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; } // Each joint contributes its point to the bounding box glm::vec3 jointPosition = extractTranslation(transforms[i]); totalExtents.addPoint(jointPosition); Shape* shape = _shapes[i]; if (!shape) { continue; } // Each joint with a shape contributes to the totalExtents: a box // that contains the sphere centered at the end of the joint with radius of the bone. // TODO: skip hand and arm shapes for bounding box calculation int type = shape->getType(); if (type == CAPSULE_SHAPE) { // add the two furthest surface points of the capsule CapsuleShape* capsule = static_cast<CapsuleShape*>(shape); float radius = capsule->getRadius(); glm::vec3 axis(radius); Extents shapeExtents; shapeExtents.reset(); shapeExtents.addPoint(jointPosition + axis); shapeExtents.addPoint(jointPosition - axis); totalExtents.addExtents(shapeExtents); } else if (type == SPHERE_SHAPE) { float radius = shape->getBoundingRadius(); glm::vec3 axis(radius); Extents shapeExtents; shapeExtents.reset(); shapeExtents.addPoint(jointPosition + axis); shapeExtents.addPoint(jointPosition - axis); totalExtents.addExtents(shapeExtents); } } // compute bounding shape parameters // NOTE: we assume that the longest side of totalExtents is the yAxis... glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; // ... and assume the radius is half the RMS of the X and Z sides: float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); }
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms int numStates = _jointStates.size(); QVector<glm::mat4> transforms; transforms.fill(glm::mat4(), numStates); QVector<VerletPoint>& ragdollPoints = _ragdoll->getPoints(); // compute the default transforms and slam the ragdoll positions accordingly // (which puts the shapes where we want them) for (int i = 0; i < numStates; i++) { JointState& state = _jointStates[i]; const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { transforms[i] = _jointStates[i].getTransform(); ragdollPoints[i].initPosition(extractTranslation(transforms[i])); continue; } glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; // setting the ragdollPoints here slams the VerletShapes into their default positions ragdollPoints[i].initPosition(extractTranslation(transforms[i])); } // compute bounding box that encloses all shapes Extents totalExtents; totalExtents.reset(); totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < _shapes.size(); i++) { Shape* shape = _shapes[i]; if (!shape) { continue; } // TODO: skip hand and arm shapes for bounding box calculation Extents shapeExtents; shapeExtents.reset(); glm::vec3 localPosition = shape->getTranslation(); int type = shape->getType(); if (type == CAPSULE_SHAPE) { // add the two furthest surface points of the capsule CapsuleShape* capsule = static_cast<CapsuleShape*>(shape); glm::vec3 axis; capsule->computeNormalizedAxis(axis); float radius = capsule->getRadius(); float halfHeight = capsule->getHalfHeight(); axis = halfHeight * axis + glm::vec3(radius); shapeExtents.addPoint(localPosition + axis); shapeExtents.addPoint(localPosition - axis); totalExtents.addExtents(shapeExtents); } else if (type == SPHERE_SHAPE) { float radius = shape->getBoundingRadius(); glm::vec3 axis = glm::vec3(radius); shapeExtents.addPoint(localPosition + axis); shapeExtents.addPoint(localPosition - axis); totalExtents.addExtents(shapeExtents); } } // compute bounding shape parameters // NOTE: we assume that the longest side of totalExtents is the yAxis... glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; // ... and assume the radius is half the RMS of the X and Z sides: float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition(); _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; _boundingRadius = 0.5f * glm::length(diagonal); }