mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
avoid bad joints when computing bounding capsule
This commit is contained in:
parent
3d7aa6f4d6
commit
4a7a384c65
4 changed files with 110 additions and 41 deletions
|
@ -86,7 +86,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
_rig->updateJointState(i, rootTransform);
|
||||
}
|
||||
|
||||
buildShapes();
|
||||
computeBoundingShape();
|
||||
|
||||
Extents meshExtents = getMeshExtents();
|
||||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||
|
@ -248,6 +248,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
||||
true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
|
@ -346,9 +347,9 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde
|
|||
}
|
||||
OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex];
|
||||
|
||||
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
||||
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
||||
glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size;
|
||||
glm::vec3 pUp = position + orientation * IDENTITY_UP * size;
|
||||
glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size;
|
||||
|
||||
glm::vec3 red(1.0f, 0.0f, 0.0f);
|
||||
geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right);
|
||||
|
@ -466,7 +467,7 @@ float MIN_JOINT_MASS = 1.0f;
|
|||
float VERY_BIG_MASS = 1.0e6f;
|
||||
|
||||
// virtual
|
||||
void SkeletonModel::buildShapes() {
|
||||
void SkeletonModel::computeBoundingShape() {
|
||||
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -476,36 +477,87 @@ void SkeletonModel::buildShapes() {
|
|||
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||
return;
|
||||
}
|
||||
computeBoundingShape(geometry);
|
||||
}
|
||||
|
||||
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||
// compute default joint transforms
|
||||
int numStates = _rig->getJointStateCount();
|
||||
QVector<glm::mat4> transforms;
|
||||
transforms.fill(glm::mat4(), numStates);
|
||||
// BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the
|
||||
// hands and feet into positions that are more correct than the default pose.
|
||||
|
||||
// Measure limb lengths so we can specify IK targets that will pull hands and feet tight to body
|
||||
QVector<QString> endEffectors;
|
||||
endEffectors.push_back("RightHand");
|
||||
endEffectors.push_back("LeftHand");
|
||||
endEffectors.push_back("RightFoot");
|
||||
endEffectors.push_back("LeftFoot");
|
||||
|
||||
QVector<QString> baseJoints;
|
||||
baseJoints.push_back("RightArm");
|
||||
baseJoints.push_back("LeftArm");
|
||||
baseJoints.push_back("RightUpLeg");
|
||||
baseJoints.push_back("LeftUpLeg");
|
||||
|
||||
for (int i = 0; i < endEffectors.size(); ++i) {
|
||||
QString tipName = endEffectors[i];
|
||||
QString baseName = baseJoints[i];
|
||||
float limbLength = 0.0f;
|
||||
int tipIndex = _rig->indexOfJoint(tipName);
|
||||
if (tipIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
// save tip's relative rotation for later
|
||||
glm::quat tipRotation = _rig->getJointState(tipIndex).getRotationInConstrainedFrame();
|
||||
|
||||
// IK on each endpoint
|
||||
int jointIndex = tipIndex;
|
||||
QVector<int> freeLineage;
|
||||
float priority = 1.0f;
|
||||
while (jointIndex > -1) {
|
||||
JointState limbJoint = _rig->getJointState(jointIndex);
|
||||
freeLineage.push_back(jointIndex);
|
||||
if (limbJoint.getName() == baseName) {
|
||||
glm::vec3 targetPosition = limbJoint.getPosition() - glm::vec3(0.0f, 1.5f * limbLength, 0.0f);
|
||||
// do IK a few times to make sure the endpoint gets close to its target
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
_rig->inverseKinematics(tipIndex,
|
||||
targetPosition,
|
||||
glm::quat(),
|
||||
priority,
|
||||
freeLineage,
|
||||
glm::mat4());
|
||||
}
|
||||
const JointState& movedState = _rig->getJointState(tipIndex);
|
||||
break;
|
||||
}
|
||||
limbLength += limbJoint.getDistanceToParent();
|
||||
jointIndex = limbJoint.getParentIndex();
|
||||
}
|
||||
|
||||
// since this IK target is totally bogus we restore the tip's relative rotation
|
||||
_rig->setJointRotationInConstrainedFrame(tipIndex, tipRotation, priority);
|
||||
}
|
||||
|
||||
// recompute all joint model-frame transforms
|
||||
glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||
_rig->updateJointState(i, rootTransform);
|
||||
}
|
||||
// END BOUNDING SHAPE HACK
|
||||
|
||||
// compute bounding box that encloses all shapes
|
||||
Extents totalExtents;
|
||||
totalExtents.reset();
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
int numStates = _rig->getJointStateCount();
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
// compute the default transform of this joint
|
||||
const JointState& state = _rig->getJointState(i);
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
transforms[i] = _rig->getJointTransform(i);
|
||||
} else {
|
||||
glm::quat modifiedRotation = state.getPreRotation() * state.getDefaultRotation() * state.getPostRotation();
|
||||
transforms[i] = transforms[parentIndex] * glm::translate(state.getTranslation())
|
||||
* state.getPreTransform() * glm::mat4_cast(modifiedRotation) * state.getPostTransform();
|
||||
}
|
||||
|
||||
// Each joint contributes a sphere at its position
|
||||
glm::vec3 axis(state.getBoneRadius());
|
||||
glm::vec3 jointPosition = extractTranslation(transforms[i]);
|
||||
totalExtents.addPoint(jointPosition + axis);
|
||||
totalExtents.addPoint(jointPosition - axis);
|
||||
// HACK WORKAROUND: ignore joints that may have bad translation (e.g. have been flagged as such with zero radius)
|
||||
if (state.getBoneRadius() > 0.0f) {
|
||||
// Each joint contributes a sphere at its position
|
||||
glm::vec3 axis(state.getBoneRadius());
|
||||
glm::vec3 jointPosition = state.getPosition();
|
||||
totalExtents.addPoint(jointPosition + axis);
|
||||
totalExtents.addPoint(jointPosition - axis);
|
||||
}
|
||||
}
|
||||
|
||||
// compute bounding shape parameters
|
||||
|
@ -517,6 +569,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
|||
|
||||
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
|
||||
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||
|
||||
// RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
_rig->restoreJointRotation(i, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
|
||||
|
@ -535,7 +592,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
|||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 axis = topPoint - bottomPoint;
|
||||
transform.setTranslation(bottomPoint);
|
||||
batch.setModelTransform(transform);
|
||||
|
|
|
@ -94,7 +94,6 @@ public:
|
|||
/// \return whether or not the head was found.
|
||||
glm::vec3 getDefaultEyeModelPosition() const;
|
||||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
||||
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
||||
|
@ -112,7 +111,7 @@ signals:
|
|||
|
||||
protected:
|
||||
|
||||
void buildShapes();
|
||||
void computeBoundingShape();
|
||||
|
||||
/// \param jointIndex index of joint in model
|
||||
/// \param position position of joint in model-frame
|
||||
|
|
|
@ -9,30 +9,31 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Rig.h"
|
||||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <queue>
|
||||
|
||||
#include "AnimationHandle.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "Rig.h"
|
||||
|
||||
void Rig::HeadParameters::dump() const {
|
||||
qCDebug(animation, "HeadParameters =");
|
||||
qCDebug(animation, " leanSideways = %0.5f", leanSideways);
|
||||
qCDebug(animation, " leanForward = %0.5f", leanForward);
|
||||
qCDebug(animation, " torsoTwist = %0.5f", torsoTwist);
|
||||
qCDebug(animation, " leanSideways = %0.5f", (double)leanSideways);
|
||||
qCDebug(animation, " leanForward = %0.5f", (double)leanForward);
|
||||
qCDebug(animation, " torsoTwist = %0.5f", (double)torsoTwist);
|
||||
glm::vec3 axis = glm::axis(localHeadOrientation);
|
||||
float theta = glm::angle(localHeadOrientation);
|
||||
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
||||
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||
axis = glm::axis(worldHeadOrientation);
|
||||
theta = glm::angle(worldHeadOrientation);
|
||||
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
||||
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||
axis = glm::axis(modelRotation);
|
||||
theta = glm::angle(modelRotation);
|
||||
qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
||||
qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", modelTranslation.x, modelTranslation.y, modelTranslation.z);
|
||||
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z);
|
||||
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z);
|
||||
qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta);
|
||||
qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", (double)modelTranslation.x, (double)modelTranslation.y, (double)modelTranslation.z);
|
||||
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", (double)eyeLookAt.x, (double)eyeLookAt.y, (double)eyeLookAt.z);
|
||||
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", (double)eyeSaccade.x, (double)eyeSaccade.y, (double)eyeSaccade.z);
|
||||
qCDebug(animation, " leanJointIndex = %.d", leanJointIndex);
|
||||
qCDebug(animation, " neckJointIndex = %.d", neckJointIndex);
|
||||
qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex);
|
||||
|
@ -103,7 +104,7 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin
|
|||
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/";
|
||||
if (role == "walk") {
|
||||
standard = base + "walk_fwd.fbx";
|
||||
} else if (role == "backup") {
|
||||
} else if (role == "backup") {
|
||||
standard = base + "walk_bwd.fbx";
|
||||
} else if (role == "leftTurn") {
|
||||
standard = base + "turn_left.fbx";
|
||||
|
|
|
@ -2677,8 +2677,21 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
averageRadius += glm::distance(vertex, averageVertex);
|
||||
}
|
||||
jointShapeInfo.averageRadius = averageRadius * radiusScale;
|
||||
jointShapeInfo.averageRadius = averageRadius * radiusScale / (float)jointShapeInfo.numVertices;
|
||||
}
|
||||
|
||||
// BUG: the boneBegin and/or boneEnd are incorrect for meshes that are "connected
|
||||
// under the bone" without weights. Unfortunately we haven't been able to find it yet.
|
||||
// Although the the mesh vertices are correct in the model-frame, the joint's transform
|
||||
// in the same frame is just BAD.
|
||||
//
|
||||
// HACK WORKAROUND: prevent these shapes from contributing to the collision capsule by setting
|
||||
// some key members of jointShapeInfo to zero:
|
||||
jointShapeInfo.numVertices = 0;
|
||||
jointShapeInfo.sumVertexWeights = 0.0f;
|
||||
jointShapeInfo.numVertexWeights = 0;
|
||||
jointShapeInfo.boneBegin = glm::vec3(0.0f);
|
||||
jointShapeInfo.averageRadius = 0.0f;
|
||||
}
|
||||
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||
|
||||
|
@ -2728,7 +2741,6 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
// the average radius to the average point.
|
||||
if (jointShapeInfo.numVertexWeights == 0
|
||||
&& jointShapeInfo.numVertices > 0) {
|
||||
jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices;
|
||||
joint.boneRadius = jointShapeInfo.averageRadius;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue