Merge pull request #2689 from AndrewMeadows/inertia

improved collision shapes for head models
This commit is contained in:
Brad Hefta-Gaub 2014-04-18 14:06:45 -07:00
commit 57a221f343
4 changed files with 103 additions and 32 deletions

View file

@ -45,7 +45,7 @@ Camera::Camera() :
_idealPosition(0.0f, 0.0f, 0.0f),
_targetPosition(0.0f, 0.0f, 0.0f),
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
_aspectRatio(16.f/9.f),
_aspectRatio(16.0f/9.0f),
_nearClip(0.08f), // default
_farClip(50.0f * TREE_SCALE), // default
_upShift(0.0f),
@ -94,8 +94,8 @@ void Camera::updateFollowMode(float deltaTime) {
// derive t from tightness
float t = _tightness * _modeShift * deltaTime;
if (t > 1.0) {
t = 1.0;
if (t > 1.0f) {
t = 1.0f;
}
// handle keepLookingAt

View file

@ -60,11 +60,11 @@ Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) {
glm::vec3 deltaScale = _scale - scale;
float scaleLength = glm::length(_scale);
float relativeDeltaScale = glm::length(_scale - scale) / scaleLength;
// decreased epsilon because this wasn't handling scale changes of 0.01
const float SMALLER_EPSILON = EPSILON * 0.0001f;
if (glm::length2(deltaScale) > SMALLER_EPSILON) {
const float ONE_PERCENT = 0.01f;
if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) {
_scale = scale;
rebuildShapes();
}
@ -468,20 +468,51 @@ void Model::clearShapes() {
void Model::rebuildShapes() {
clearShapes();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (_jointStates.isEmpty()) {
if (geometry.joints.isEmpty()) {
return;
}
// make sure all the joints are updated correctly before we try to create their shapes
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int numJoints = geometry.joints.size();
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numJoints);
QVector<glm::quat> combinedRotations;
combinedRotations.fill(glm::quat(), numJoints);
QVector<bool> shapeIsSet;
shapeIsSet.fill(false, numJoints);
int rootIndex = 0;
float uniformScale = extractUniformScale(_scale);
glm::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 rootPosition(0.f);
int numShapesSet = 0;
int lastNumShapesSet = -1;
while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) {
lastNumShapesSet = numShapesSet;
for (int i = 0; i < numJoints; ++i) {
if (shapeIsSet[i]) {
continue;
}
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
rootIndex = i;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * uniformScale * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = baseTransform * geometry.offset * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = _rotation * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
} else if (shapeIsSet[parentIndex]) {
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = combinedRotations[parentIndex] * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
}
}
}
// joint shapes
Extents totalExtents;
@ -489,48 +520,70 @@ void Model::rebuildShapes() {
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition);
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
glm::vec3 worldPosition = extractTranslation(transforms[i]);
Extents shapeExtents;
shapeExtents.reset();
if (joint.parentIndex == -1) {
rootPosition = worldPosition;
}
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
if (joint.shapeType == Shape::CAPSULE_SHAPE && halfHeight > EPSILON) {
Shape::Type type = joint.shapeType;
if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) {
// this capsule is effectively a sphere
type = Shape::SPHERE_SHAPE;
}
if (type == Shape::CAPSULE_SHAPE) {
CapsuleShape* capsule = new CapsuleShape(radius, halfHeight);
capsule->setPosition(worldPosition);
capsule->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
capsule->setRotation(combinedRotations[i] * joint.shapeRotation);
_jointShapes.push_back(capsule);
glm::vec3 endPoint;
capsule->getEndPoint(endPoint);
glm::vec3 startPoint;
capsule->getStartPoint(startPoint);
glm::vec3 axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint);
// add some points that bound a sphere at the center of the capsule
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
} else {
// add the two furthest surface points of the capsule
axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
totalExtents.addExtents(shapeExtents);
} else if (type == Shape::SPHERE_SHAPE) {
SphereShape* sphere = new SphereShape(radius, worldPosition);
_jointShapes.push_back(sphere);
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
totalExtents.addExtents(shapeExtents);
} else {
// this shape type is not handled and the joint shouldn't collide,
// however we must have a shape for each joint,
// so we make a bogus sphere with zero radius.
// TODO: implement collision groups for more control over what collides with what
SphereShape* sphere = new SphereShape(0.f, worldPosition);
_jointShapes.push_back(sphere);
}
totalExtents.addExtents(shapeExtents);
}
// bounding shape
// NOTE: we assume that the longest side of totalExtents is the yAxis
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
float capsuleRadius = 0.25f * (diagonal.x + diagonal.z); // half the average of x and z
// 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::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 rootPosition = extractTranslation(transforms[rootIndex]);
_boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition);
_boundingShape.setPosition(_translation - _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
void Model::updateShapePositions() {
@ -557,6 +610,7 @@ void Model::updateShapePositions() {
_boundingRadius = sqrtf(_boundingRadius);
_shapesAreDirty = false;
_boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
}

View file

@ -1624,7 +1624,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd));
float totalWeight = 0.0f;
for (int j = 0; j < cluster.indices.size(); j++) {
@ -1686,7 +1685,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
}
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) {
@ -1722,6 +1720,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
FBXJoint& joint = geometry.joints[i];
JointShapeInfo& jointShapeInfo = jointShapeInfos[i];
if (joint.parentIndex == -1) {
jointShapeInfo.boneBegin = glm::vec3(0.0f);
} 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));
}
// we use a capsule if the joint ANY mesh vertices successfully projected onto the bone
// AND its boneRadius is not too close to zero
bool collideLikeCapsule = jointShapeInfo.numProjectedVertices > 0
@ -1733,12 +1739,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
joint.shapeType = Shape::CAPSULE_SHAPE;
} else {
// collide the joint like a sphere
joint.shapeType = Shape::SPHERE_SHAPE;
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) {
@ -1747,6 +1753,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices;
joint.boneRadius = jointShapeInfo.averageRadius;
}
float distanceFromEnd = glm::length(joint.shapePosition);
float distanceFromBegin = glm::distance(joint.shapePosition, jointShapeInfo.boneBegin);
if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) {
// The shape is further from both joint endpoints than the endpoints are from each other
// which probably means the model has a bad transform somewhere. We disable this shape
// by setting its type to UNKNOWN_SHAPE.
joint.shapeType = Shape::UNKNOWN_SHAPE;
}
}
}
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());

View file

@ -18,6 +18,8 @@
#include <QVariant>
#include <QVector>
#include <Shape.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -91,7 +93,7 @@ public:
QString name;
glm::vec3 shapePosition; // in joint frame
glm::quat shapeRotation; // in joint frame
int shapeType;
Shape::Type shapeType;
};