mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 01:24:03 +02:00
Merge pull request #2689 from AndrewMeadows/inertia
improved collision shapes for head models
This commit is contained in:
commit
57a221f343
4 changed files with 103 additions and 32 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue