mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 16:23:39 +02:00
Add JointShapeInfo for joint shape calculations
This commit is contained in:
parent
8ef657e5b2
commit
1c0826d696
3 changed files with 109 additions and 38 deletions
|
@ -22,6 +22,7 @@
|
|||
#include <OctalCode.h>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <Shape.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
@ -34,6 +35,22 @@ std::ostream& operator<<(std::ostream& s, const glm::vec3& v) {
|
|||
|
||||
using namespace std;
|
||||
|
||||
void Extents::reset() {
|
||||
minimum = glm::vec3(FLT_MAX);
|
||||
maximum = glm::vec3(-FLT_MAX);
|
||||
}
|
||||
|
||||
bool Extents::containsPoint(const glm::vec3& point) const {
|
||||
return (point.x >= minimum.x && point.x <= maximum.x
|
||||
&& point.y >= minimum.y && point.y <= maximum.y
|
||||
&& point.z >= minimum.z && point.z <= maximum.z);
|
||||
}
|
||||
|
||||
void Extents::addPoint(const glm::vec3& point) {
|
||||
minimum = glm::min(minimum, point);
|
||||
maximum = glm::max(maximum, point);
|
||||
}
|
||||
|
||||
template<class T> QVariant readBinaryArray(QDataStream& in) {
|
||||
quint32 arrayLength;
|
||||
quint32 encoding;
|
||||
|
@ -847,6 +864,21 @@ QString getString(const QVariant& value) {
|
|||
return list.isEmpty() ? value.toString() : list.at(0).toString();
|
||||
}
|
||||
|
||||
class JointShapeInfo {
|
||||
public:
|
||||
JointShapeInfo() : numVertices(0), numProjectedVertices(0), averageVertex(0.f), boneBegin(0.f), averageRadius(0.f) {
|
||||
extents.reset();
|
||||
}
|
||||
|
||||
// NOTE: the points here are in the "joint frame" which has the "jointEnd" at the origin
|
||||
int numVertices; // num vertices from contributing meshes
|
||||
int numProjectedVertices; // num vertices that successfully project onto bone axis
|
||||
Extents extents; // max and min extents of mesh vertices (in joint frame)
|
||||
glm::vec3 averageVertex; // average of all mesh vertices (in joint frame)
|
||||
glm::vec3 boneBegin; // parent joint location (in joint frame)
|
||||
float averageRadius; // average distance from mesh points to averageVertex
|
||||
};
|
||||
|
||||
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
|
@ -1256,14 +1288,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
joint.boneRadius = 0.0f;
|
||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||
joint.name = model.name;
|
||||
joint.extents.minimum = glm::vec3(FLT_MAX);
|
||||
joint.extents.maximum = glm::vec3(-FLT_MAX);
|
||||
joint.averageVertex = glm::vec3(0.f);
|
||||
joint.numVertices = 0;
|
||||
joint.averageRadius = 0.f;
|
||||
joint.shapePosition = glm::vec3(0.f);
|
||||
joint.shapeType = Shape::UNKNOWN_SHAPE;
|
||||
geometry.joints.append(joint);
|
||||
geometry.jointIndices.insert(model.name, geometry.joints.size() - 1);
|
||||
}
|
||||
// for each joint we allocate a JointShapeInfo in which we'll store collision shape info
|
||||
QVector<JointShapeInfo> jointShapeInfos;
|
||||
jointShapeInfos.resize(geometry.joints.size());
|
||||
|
||||
// find our special joints
|
||||
geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID);
|
||||
|
@ -1285,10 +1317,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]);
|
||||
}
|
||||
|
||||
geometry.bindExtents.minimum = glm::vec3(FLT_MAX);
|
||||
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX);
|
||||
geometry.staticExtents.minimum = glm::vec3(FLT_MAX);
|
||||
geometry.staticExtents.maximum = glm::vec3(-FLT_MAX);
|
||||
geometry.bindExtents.reset();
|
||||
geometry.staticExtents.reset();
|
||||
|
||||
QVariantHash springs = mapping.value("spring").toHash();
|
||||
QVariant defaultSpring = springs.value("default");
|
||||
|
@ -1404,8 +1434,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
|
||||
// update the bind pose extents
|
||||
glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform);
|
||||
geometry.bindExtents.minimum = glm::min(geometry.bindExtents.minimum, bindTranslation);
|
||||
geometry.bindExtents.maximum = glm::max(geometry.bindExtents.maximum, bindTranslation);
|
||||
geometry.bindExtents.addPoint(bindTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1448,9 +1477,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
boneDirection /= boneLength;
|
||||
}
|
||||
}
|
||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
||||
jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd));
|
||||
|
||||
bool jointIsStatic = joint.freeLineage.isEmpty();
|
||||
glm::vec3 jointTranslation = extractTranslation(geometry.offset * joint.bindTransform);
|
||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
||||
float totalWeight = 0.0f;
|
||||
for (int j = 0; j < cluster.indices.size(); j++) {
|
||||
int oldIndex = cluster.indices.at(j);
|
||||
|
@ -1464,17 +1496,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
const glm::vec3& vertex = extracted.mesh.vertices.at(it.value());
|
||||
float proj = glm::dot(boneDirection, vertex - boneEnd);
|
||||
if (proj < 0.0f && proj > -boneLength) {
|
||||
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(vertex, boneEnd + boneDirection * proj));
|
||||
joint.boneRadius = glm::max(joint.boneRadius,
|
||||
radiusScale * glm::distance(vertex, boneEnd + boneDirection * proj));
|
||||
++jointShapeInfo.numProjectedVertices;
|
||||
}
|
||||
glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd));
|
||||
joint.extents.minimum = glm::min(joint.extents.minimum, vertexInJointFrame);
|
||||
joint.extents.maximum = glm::max(joint.extents.maximum, vertexInJointFrame);
|
||||
joint.averageVertex += vertexInJointFrame;
|
||||
++joint.numVertices;
|
||||
jointShapeInfo.extents.addPoint(vertexInJointFrame);
|
||||
jointShapeInfo.averageVertex += vertexInJointFrame;
|
||||
++jointShapeInfo.numVertices;
|
||||
if (jointIsStatic) {
|
||||
// expand the extents of static (nonmovable) joints
|
||||
geometry.staticExtents.minimum = glm::min(geometry.staticExtents.minimum, vertex + jointTranslation);
|
||||
geometry.staticExtents.maximum = glm::max(geometry.staticExtents.maximum, vertex + jointTranslation);
|
||||
geometry.staticExtents.addPoint(vertex + jointTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1497,40 +1529,47 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
} else {
|
||||
int jointIndex = maxJointIndex;
|
||||
FBXJoint& joint = geometry.joints[jointIndex];
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
|
||||
|
||||
glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform;
|
||||
glm::quat rotateMeshToJoint = glm::inverse(extractRotation(transformJointToMesh));
|
||||
glm::vec3 boneEnd = extractTranslation(transformJointToMesh);
|
||||
glm::vec3 boneBegin = boneEnd;
|
||||
|
||||
glm::vec3 boneDirection;
|
||||
float boneLength;
|
||||
if (joint.parentIndex != -1) {
|
||||
boneDirection = boneEnd - extractTranslation(inverseModelTransform *
|
||||
geometry.joints[joint.parentIndex].bindTransform);
|
||||
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);
|
||||
jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd));
|
||||
|
||||
glm::vec3 averageVertex(0.f);
|
||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
float proj = glm::dot(boneDirection, vertex - boneEnd);
|
||||
if (proj < 0.0f && proj > -boneLength) {
|
||||
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(vertex, boneEnd + boneDirection * proj));
|
||||
++jointShapeInfo.numProjectedVertices;
|
||||
}
|
||||
glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd));
|
||||
joint.extents.minimum = glm::min(joint.extents.minimum, vertexInJointFrame);
|
||||
joint.extents.maximum = glm::max(joint.extents.maximum, vertexInJointFrame);
|
||||
joint.averageVertex += vertexInJointFrame;
|
||||
jointShapeInfo.extents.addPoint(vertexInJointFrame);
|
||||
jointShapeInfo.averageVertex += vertexInJointFrame;
|
||||
averageVertex += vertex;
|
||||
}
|
||||
int numVertices = extracted.mesh.vertices.size();
|
||||
joint.numVertices = numVertices;
|
||||
jointShapeInfo.numVertices = numVertices;
|
||||
if (numVertices > 0) {
|
||||
averageVertex /= float(joint.numVertices);
|
||||
averageVertex /= float(jointShapeInfo.numVertices);
|
||||
float averageRadius = 0.f;
|
||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
averageRadius += glm::distance(vertex, averageVertex);
|
||||
}
|
||||
joint.boneRadius = averageRadius * (radiusScale / float(numVertices));
|
||||
jointShapeInfo.averageRadius = averageRadius * radiusScale;
|
||||
}
|
||||
}
|
||||
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
|
||||
|
@ -1580,11 +1619,38 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
geometry.meshes.append(extracted.mesh);
|
||||
}
|
||||
|
||||
// now that all joints have been scanned, divide the averages by number of vertices
|
||||
// now that all joints have been scanned, compute a collision shape for each joint
|
||||
glm::vec3 yAxis(0.f, 1.f, 0.f);
|
||||
for (int i = 0; i < geometry.joints.size(); ++i) {
|
||||
FBXJoint& joint = geometry.joints[i];
|
||||
if (joint.numVertices > 0) {
|
||||
joint.averageVertex /= float(joint.numVertices);
|
||||
JointShapeInfo& jointShapeInfo = jointShapeInfos[i];
|
||||
|
||||
// we use a capsule if the joint ANY mesh vertices that successfully projected onto the bone
|
||||
// AND its boneRadius is not too close to zero
|
||||
bool collideLikeCapsule = jointShapeInfo.numProjectedVertices > 0
|
||||
&& glm::length(jointShapeInfo.boneBegin) > EPSILON;
|
||||
|
||||
if (collideLikeCapsule) {
|
||||
joint.shapeRotation = rotationBetween(yAxis, jointShapeInfo.boneBegin);
|
||||
joint.shapePosition = 0.5f * jointShapeInfo.boneBegin;
|
||||
//joint.shapePosition = glm::vec3(0.f);
|
||||
joint.shapeType = Shape::CAPSULE_SHAPE;
|
||||
} else {
|
||||
// collide the joint like a sphere
|
||||
if (jointShapeInfo.numVertices > 0) {
|
||||
jointShapeInfo.averageVertex /= float(jointShapeInfo.numVertices);
|
||||
joint.shapePosition = jointShapeInfo.averageVertex;
|
||||
} else {
|
||||
joint.shapePosition = glm::vec3(0.f);
|
||||
joint.shapeType = Shape::SPHERE_SHAPE;
|
||||
}
|
||||
if (jointShapeInfo.numProjectedVertices == 0
|
||||
&& jointShapeInfo.numVertices > 0) {
|
||||
// the bone projection algorithm was not able to compute the joint radius
|
||||
// so we use an alternative measure
|
||||
jointShapeInfo.averageRadius /= float(jointShapeInfo.numVertices);
|
||||
joint.boneRadius = jointShapeInfo.averageRadius;
|
||||
}
|
||||
}
|
||||
}
|
||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||
|
|
|
@ -26,7 +26,10 @@ extern const char* FACESHIFT_BLENDSHAPES[];
|
|||
|
||||
class Extents {
|
||||
public:
|
||||
|
||||
//Extents() : minimum(FLT_MAX), maximum(-FLT_MAX) {}
|
||||
void reset();
|
||||
void addPoint(const glm::vec3& point);
|
||||
bool containsPoint(const glm::vec3& point) const;
|
||||
glm::vec3 minimum;
|
||||
glm::vec3 maximum;
|
||||
};
|
||||
|
@ -70,13 +73,15 @@ public:
|
|||
glm::quat inverseDefaultRotation;
|
||||
glm::quat inverseBindRotation;
|
||||
glm::mat4 bindTransform;
|
||||
// TODO: add some comments to these data members
|
||||
// Trying to provide enough info so that the proper shape can be generated in Model
|
||||
QString name;
|
||||
Extents extents;
|
||||
int numVertices;
|
||||
glm::vec3 averageVertex;
|
||||
float averageRadius;
|
||||
glm::vec3 shapePosition; // in joint frame (where boneEnd = origin)
|
||||
glm::quat shapeRotation;
|
||||
int shapeType;
|
||||
};
|
||||
|
||||
|
||||
/// A single binding to a joint in an FBX document.
|
||||
class FBXCluster {
|
||||
public:
|
||||
|
|
|
@ -138,7 +138,7 @@ void Model::createCollisionShapes() {
|
|||
float uniformScale = extractUniformScale(_scale);
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
const FBXJoint& joint = geometry.joints[i];
|
||||
glm::vec3 meshCenter = _jointStates[i].combinedRotation * joint.averageVertex;
|
||||
glm::vec3 meshCenter = _jointStates[i].combinedRotation * joint.shapePosition;
|
||||
glm::vec3 position = _rotation * (extractTranslation(_jointStates[i].transform) + uniformScale * meshCenter) + _translation;
|
||||
|
||||
float radius = uniformScale * joint.boneRadius;
|
||||
|
@ -154,7 +154,7 @@ void Model::updateShapePositions() {
|
|||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
const FBXJoint& joint = geometry.joints[i];
|
||||
// shape positions are stored in world-frame
|
||||
glm::vec3 meshCenter = _jointStates[i].combinedRotation * joint.averageVertex;
|
||||
glm::vec3 meshCenter = _jointStates[i].combinedRotation * joint.shapePosition;
|
||||
_shapes[i]->setPosition(_rotation * (extractTranslation(_jointStates[i].transform) + uniformScale * meshCenter) + _translation);
|
||||
_shapes[i]->setRotation(_jointStates[i].combinedRotation);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue