mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 09:33:11 +02:00
fix avatar bounding capsule calculations
This commit is contained in:
parent
90fe54e6d6
commit
281e4f21fc
6 changed files with 98 additions and 108 deletions
|
@ -544,14 +544,20 @@ void SkeletonModel::computeBoundingShape() {
|
|||
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);
|
||||
|
||||
// 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);
|
||||
const glm::mat4& jointTransform = state.getTransform();
|
||||
float scale = extractUniformScale(jointTransform);
|
||||
|
||||
// Each joint contributes a capsule defined by FBXJoint.shapeInfo.
|
||||
// For totalExtents we use the capsule endpoints expanded by the radius.
|
||||
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo;
|
||||
for (int j = 0; j < shapeInfo.points.size(); ++j) {
|
||||
glm::vec3 transformedPoint = extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]));
|
||||
vec3 radius(scale * shapeInfo.radius);
|
||||
totalExtents.addPoint(transformedPoint + radius);
|
||||
totalExtents.addPoint(transformedPoint - radius);
|
||||
}
|
||||
}
|
||||
|
||||
// compute bounding shape parameters
|
||||
|
|
|
@ -41,7 +41,7 @@ void JointState::copyState(const JointState& other) {
|
|||
// DO NOT copy _constraint
|
||||
_name = other._name;
|
||||
_isFree = other._isFree;
|
||||
_boneRadius = other._boneRadius;
|
||||
// _boneRadius = other._boneRadius;
|
||||
_parentIndex = other._parentIndex;
|
||||
_defaultRotation = other._defaultRotation;
|
||||
_inverseDefaultRotation = other._inverseDefaultRotation;
|
||||
|
@ -58,7 +58,7 @@ JointState::JointState(const FBXJoint& joint) {
|
|||
_rotationInConstrainedFrame = joint.rotation;
|
||||
_name = joint.name;
|
||||
_isFree = joint.isFree;
|
||||
_boneRadius = joint.boneRadius;
|
||||
// _boneRadius = joint.boneRadius;
|
||||
_parentIndex = joint.parentIndex;
|
||||
_translation = joint.translation;
|
||||
_defaultRotation = joint.rotation;
|
||||
|
|
|
@ -118,7 +118,7 @@ public:
|
|||
const glm::quat& getDefaultRotation() const { return _defaultRotation; }
|
||||
const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; }
|
||||
const QString& getName() const { return _name; }
|
||||
float getBoneRadius() const { return _boneRadius; }
|
||||
// float getBoneRadius() const { return _boneRadius; }
|
||||
bool getIsFree() const { return _isFree; }
|
||||
float getAnimationPriority() const { return _animationPriority; }
|
||||
void setAnimationPriority(float priority) { _animationPriority = priority; }
|
||||
|
@ -149,7 +149,7 @@ private:
|
|||
QString _name;
|
||||
int _parentIndex;
|
||||
bool _isFree;
|
||||
float _boneRadius;
|
||||
// float _boneRadius;
|
||||
glm::vec3 _rotationMin;
|
||||
glm::vec3 _rotationMax;
|
||||
glm::quat _preRotation;
|
||||
|
|
|
@ -1257,21 +1257,7 @@ QString getString(const QVariant& value) {
|
|||
return list.isEmpty() ? value.toString() : list.at(0).toString();
|
||||
}
|
||||
|
||||
class JointShapeInfo {
|
||||
public:
|
||||
JointShapeInfo() : numVertices(0),
|
||||
sumVertexWeights(0.0f), sumWeightedRadii(0.0f), numVertexWeights(0),
|
||||
boneBegin(0.0f), averageRadius(0.0f) {
|
||||
}
|
||||
|
||||
// NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin
|
||||
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 boneBegin; // parent joint location (in joint frame)
|
||||
float averageRadius;
|
||||
};
|
||||
typedef std::vector<glm::vec3> ShapeVertices;
|
||||
|
||||
class AnimationCurve {
|
||||
public:
|
||||
|
@ -2282,22 +2268,21 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
joint.postTransform = model.postTransform;
|
||||
joint.rotationMin = model.rotationMin;
|
||||
joint.rotationMax = model.rotationMax;
|
||||
glm::quat combinedRotation = model.preRotation * model.rotation * model.postRotation;
|
||||
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
if (joint.parentIndex == -1) {
|
||||
joint.transform = geometry.offset * glm::translate(model.translation) * model.preTransform *
|
||||
glm::mat4_cast(combinedRotation) * model.postTransform;
|
||||
joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform *
|
||||
glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||
joint.inverseDefaultRotation = glm::inverse(combinedRotation);
|
||||
joint.distanceToParent = 0.0f;
|
||||
joint.distanceToParent = 0.0f;
|
||||
|
||||
} else {
|
||||
const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex);
|
||||
joint.transform = parentJoint.transform * glm::translate(model.translation) *
|
||||
model.preTransform * glm::mat4_cast(combinedRotation) * model.postTransform;
|
||||
joint.transform = parentJoint.transform * glm::translate(joint.translation) *
|
||||
joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
|
||||
joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation;
|
||||
joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform),
|
||||
extractTranslation(joint.transform));
|
||||
}
|
||||
joint.boneRadius = 0.0f;
|
||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||
joint.name = model.name;
|
||||
|
||||
|
@ -2326,9 +2311,10 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
zCurve.values.isEmpty() ? defaultValues.z : zCurve.values.at(i % zCurve.values.size()))));
|
||||
}
|
||||
}
|
||||
// for each joint we allocate a JointShapeInfo in which we'll store collision shape info
|
||||
QVector<JointShapeInfo> jointShapeInfos;
|
||||
jointShapeInfos.resize(geometry.joints.size());
|
||||
|
||||
// NOTE: shapeVertices are in joint-frame
|
||||
QVector<ShapeVertices> shapeVertices;
|
||||
shapeVertices.resize(geometry.joints.size());
|
||||
|
||||
// find our special joints
|
||||
geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID);
|
||||
|
@ -2585,8 +2571,10 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
boneDirection /= boneLength;
|
||||
}
|
||||
}
|
||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
||||
|
||||
float clusterScale = extractUniformScale(fbxCluster.inverseBindMatrix);
|
||||
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
|
||||
ShapeVertices& points = shapeVertices[jointIndex];
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
for (int j = 0; j < cluster.indices.size(); j++) {
|
||||
|
@ -2595,18 +2583,13 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
totalWeight += weight;
|
||||
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
|
||||
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
||||
// expand the bone radius for vertices with at least 1/4 weight
|
||||
|
||||
// remember vertices with at least 1/4 weight
|
||||
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, boneEnd - vertex);
|
||||
float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f * weight : weight;
|
||||
|
||||
jointShapeInfo.sumVertexWeights += radiusWeight;
|
||||
jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj);
|
||||
++jointShapeInfo.numVertexWeights;
|
||||
|
||||
++jointShapeInfo.numVertices;
|
||||
// transform to joint-frame and save for later
|
||||
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value()));
|
||||
points.push_back(extractTranslation(vertexTransform) * clusterScale);
|
||||
}
|
||||
|
||||
// look for an unused slot in the weights vector
|
||||
|
@ -2649,54 +2632,16 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
// this is a single-mesh joint
|
||||
int jointIndex = maxJointIndex;
|
||||
FBXJoint& joint = geometry.joints[jointIndex];
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
||||
|
||||
glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform;
|
||||
glm::vec3 boneEnd = extractTranslation(transformJointToMesh);
|
||||
glm::vec3 boneBegin = boneEnd;
|
||||
|
||||
glm::vec3 boneDirection;
|
||||
float boneLength = 0.0f;
|
||||
if (joint.parentIndex != -1) {
|
||||
boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform);
|
||||
boneDirection = boneEnd - boneBegin;
|
||||
boneLength = glm::length(boneDirection);
|
||||
if (boneLength > EPSILON) {
|
||||
boneDirection /= boneLength;
|
||||
}
|
||||
}
|
||||
float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix);
|
||||
|
||||
// compute average vertex
|
||||
glm::vec3 averageVertex(0.0f);
|
||||
// transform cluster vertices to joint-frame and save for later
|
||||
float clusterScale = extractUniformScale(firstFBXCluster.inverseBindMatrix);
|
||||
glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform;
|
||||
ShapeVertices& points = shapeVertices[jointIndex];
|
||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
float proj = glm::dot(boneDirection, boneEnd - vertex);
|
||||
float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f : 1.0f;
|
||||
jointShapeInfo.sumVertexWeights += radiusWeight;
|
||||
jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj);
|
||||
++jointShapeInfo.numVertexWeights;
|
||||
averageVertex += vertex;
|
||||
const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex);
|
||||
points.push_back(extractTranslation(vertexTransform) * clusterScale);
|
||||
}
|
||||
|
||||
// compute joint's radius
|
||||
int numVertices = extracted.mesh.vertices.size();
|
||||
jointShapeInfo.numVertices = numVertices;
|
||||
if (numVertices > 0) {
|
||||
// compute average radius
|
||||
averageVertex /= (float)jointShapeInfo.numVertices;
|
||||
float averageRadius = 0.0f;
|
||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
averageRadius += glm::distance(vertex, averageVertex);
|
||||
}
|
||||
averageRadius *= radiusScale / (float)jointShapeInfo.numVertices;
|
||||
|
||||
// final radius is minimum of average and weighted
|
||||
float weightedRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights;
|
||||
jointShapeInfo.averageRadius = glm::min(weightedRadius, averageRadius);
|
||||
}
|
||||
|
||||
// clear sumVertexWeights (this flags it as a single-mesh joint for later)
|
||||
jointShapeInfo.sumVertexWeights = 0.0f;
|
||||
}
|
||||
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||
|
||||
|
@ -2721,24 +2666,59 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
|||
glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f);
|
||||
for (int i = 0; i < geometry.joints.size(); ++i) {
|
||||
FBXJoint& joint = geometry.joints[i];
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[i];
|
||||
|
||||
if (joint.parentIndex == -1) {
|
||||
jointShapeInfo.boneBegin = glm::vec3(0.0f);
|
||||
// NOTE: points are in joint-frame
|
||||
// compute average point
|
||||
ShapeVertices& points = shapeVertices[i];
|
||||
glm::vec3 avgPoint = glm::vec3(0.0f);
|
||||
for (uint32_t j = 0; j < points.size(); ++j) {
|
||||
avgPoint += points[j];
|
||||
}
|
||||
avgPoint /= (float)points.size();
|
||||
|
||||
// compute axis from begin to avgPoint
|
||||
glm::vec3 begin(0.0f);
|
||||
glm::vec3 end = avgPoint;
|
||||
glm::vec3 axis = end - begin;
|
||||
float axisLength = glm::length(axis);
|
||||
if (axisLength > EPSILON) {
|
||||
axis /= axisLength;
|
||||
} else {
|
||||
const FBXJoint& parentJoint = geometry.joints[joint.parentIndex];
|
||||
glm::quat inverseRotation = glm::inverse(extractRotation(joint.transform));
|
||||
jointShapeInfo.boneBegin = inverseRotation * (extractTranslation(parentJoint.transform) - extractTranslation(joint.transform));
|
||||
axis = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
if (jointShapeInfo.sumVertexWeights > 0.0f) {
|
||||
// mutiple meshes contributed to the bone radius and now that all
|
||||
// contributing meshes are done we can finally compute the boneRadius
|
||||
joint.boneRadius = jointShapeInfo.sumWeightedRadii / jointShapeInfo.sumVertexWeights;
|
||||
} else {
|
||||
// single-mesh joint
|
||||
joint.boneRadius = jointShapeInfo.averageRadius;
|
||||
// measure average cylindrical radius
|
||||
float avgRadius = 0.0f;
|
||||
if (points.size() > 0) {
|
||||
float minProjection = FLT_MAX;
|
||||
float maxProjection = -FLT_MIN;
|
||||
for (uint32_t j = 0; j < points.size(); ++j) {
|
||||
glm::vec3 offset = points[j] - avgPoint;
|
||||
float projection = glm::dot(offset, axis);
|
||||
maxProjection = glm::max(maxProjection, projection);
|
||||
minProjection = glm::min(minProjection, projection);
|
||||
avgRadius += glm::length(offset - projection * axis);
|
||||
}
|
||||
avgRadius /= (float)points.size();
|
||||
|
||||
// compute endpoints of capsule in joint-frame
|
||||
glm::vec3 capsuleBegin = avgPoint;
|
||||
glm::vec3 capsuleEnd = avgPoint;
|
||||
if (maxProjection - minProjection < 2.0f * avgRadius) {
|
||||
// the mesh-as-cylinder approximation is too short to collide as a capsule
|
||||
// so we'll collapse it to a sphere (although that isn't a very good approximation)
|
||||
capsuleBegin = avgPoint + 0.5f * (maxProjection + minProjection) * axis;
|
||||
capsuleEnd = capsuleBegin;
|
||||
} else {
|
||||
capsuleBegin = avgPoint + (minProjection + avgRadius) * axis;
|
||||
capsuleEnd = avgPoint + (maxProjection - avgRadius) * axis;
|
||||
}
|
||||
|
||||
// save points for later
|
||||
joint.shapeInfo.points.push_back(capsuleBegin);
|
||||
joint.shapeInfo.points.push_back(capsuleEnd);
|
||||
}
|
||||
joint.shapeInfo.radius = avgRadius;
|
||||
}
|
||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||
|
||||
|
|
|
@ -55,15 +55,21 @@ public:
|
|||
QVector<glm::vec3> normals;
|
||||
};
|
||||
|
||||
struct FBXJointShapeInfo {
|
||||
// same units and frame as FBXJoint.translation
|
||||
QVector<glm::vec3> points;
|
||||
float radius;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node) extracted from an FBX document.
|
||||
class FBXJoint {
|
||||
public:
|
||||
|
||||
bool isFree;
|
||||
FBXJointShapeInfo shapeInfo;
|
||||
QVector<int> freeLineage;
|
||||
bool isFree;
|
||||
int parentIndex;
|
||||
float distanceToParent;
|
||||
float boneRadius;
|
||||
|
||||
// http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html
|
||||
|
||||
|
|
|
@ -444,7 +444,6 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
|||
geometry.joints[0].isFree = false;
|
||||
geometry.joints[0].parentIndex = -1;
|
||||
geometry.joints[0].distanceToParent = 0;
|
||||
geometry.joints[0].boneRadius = 0;
|
||||
geometry.joints[0].translation = glm::vec3(0, 0, 0);
|
||||
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
|
||||
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
|
||||
|
@ -620,7 +619,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
|
|||
qCDebug(modelformat) << " freeLineage" << joint.freeLineage;
|
||||
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
||||
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
||||
qCDebug(modelformat) << " boneRadius" << joint.boneRadius;
|
||||
qCDebug(modelformat) << " translation" << joint.translation;
|
||||
qCDebug(modelformat) << " preTransform" << joint.preTransform;
|
||||
qCDebug(modelformat) << " preRotation" << joint.preRotation;
|
||||
|
|
Loading…
Reference in a new issue