From 3acc518e8a3f8dcbed27f699a7471f75fa373232 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Apr 2014 15:18:15 -0700 Subject: [PATCH 1/4] make float literals obey the coding standard --- interface/src/Camera.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 2ef32dac5f..f9ee5bdd25 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -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 From 3072161a303e43b69847ececadb97623304e0b9c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Apr 2014 15:18:46 -0700 Subject: [PATCH 2/4] improved collision shapes of models --- interface/src/renderer/FBXReader.cpp | 21 ++++++++++++++++++--- interface/src/renderer/Model.cpp | 26 ++++++++++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 959c015eea..32b5686558 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1516,7 +1516,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++) { @@ -1578,7 +1577,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) { @@ -1614,6 +1612,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 @@ -1625,12 +1631,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) { @@ -1639,6 +1645,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 radius to zero. + joint.boneRadius = 0.0f; + } } } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 2d47a077b7..bcde861e84 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -510,24 +510,38 @@ void Model::rebuildShapes() { 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); + + // 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 { SphereShape* sphere = new SphereShape(radius, worldPosition); _jointShapes.push_back(sphere); - glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(worldPosition + axis); - shapeExtents.addPoint(worldPosition - axis); + if (radius > 0.0f) { + // only include sphere shapes with non-zero radius + glm::vec3 axis = glm::vec3(radius); + shapeExtents.addPoint(worldPosition + axis); + shapeExtents.addPoint(worldPosition - axis); + totalExtents.addExtents(shapeExtents); + } } - 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); _boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition); From cfdbdad2d8823dcd7d4591502049b1a861ce4e2f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 17 Apr 2014 16:22:35 -0700 Subject: [PATCH 3/4] improved method for disabling bad joint shapes --- interface/src/renderer/FBXReader.cpp | 4 ++-- interface/src/renderer/FBXReader.h | 4 +++- interface/src/renderer/Model.cpp | 28 +++++++++++++++++++--------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 32b5686558..ff3fa8667f 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1651,8 +1651,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) 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 radius to zero. - joint.boneRadius = 0.0f; + // by setting its type to UNKNOWN_SHAPE. + joint.shapeType = Shape::UNKNOWN_SHAPE; } } } diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 73c305e2eb..366ab12180 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -18,6 +18,8 @@ #include #include +#include + #include #include @@ -91,7 +93,7 @@ public: QString name; glm::vec3 shapePosition; // in joint frame glm::quat shapeRotation; // in joint frame - int shapeType; + Shape::Type shapeType; }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index bcde861e84..ca0b46841d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -500,7 +500,12 @@ void Model::rebuildShapes() { 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); @@ -523,18 +528,23 @@ void Model::rebuildShapes() { totalExtents.addExtents(shapeExtents); - } else { + } else if (type == Shape::SPHERE_SHAPE) { SphereShape* sphere = new SphereShape(radius, worldPosition); _jointShapes.push_back(sphere); - if (radius > 0.0f) { - // only include sphere shapes with non-zero radius - glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(worldPosition + axis); - shapeExtents.addPoint(worldPosition - axis); - totalExtents.addExtents(shapeExtents); - } + 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 and put it at the center of the model + // TODO: implement collision groups for more control over what collides with what + SphereShape* sphere = new SphereShape(0.f, _offset); + _jointShapes.push_back(sphere); } + } // bounding shape From 26487aad0682f0a1c845fcce0467ad2685f67b9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 18 Apr 2014 11:19:21 -0700 Subject: [PATCH 4/4] more correct bounding shapes for Models --- interface/src/renderer/Model.cpp | 78 ++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index ca0b46841d..f8de7210ea 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -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 transforms; + transforms.fill(glm::mat4(), numJoints); + QVector combinedRotations; + combinedRotations.fill(glm::quat(), numJoints); + QVector 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,15 +520,10 @@ 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; Shape::Type type = joint.shapeType; @@ -508,7 +534,7 @@ void Model::rebuildShapes() { 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; @@ -526,7 +552,6 @@ void Model::rebuildShapes() { shapeExtents.addPoint(worldPosition + axis); shapeExtents.addPoint(worldPosition - axis); - totalExtents.addExtents(shapeExtents); } else if (type == Shape::SPHERE_SHAPE) { SphereShape* sphere = new SphereShape(radius, worldPosition); @@ -539,12 +564,11 @@ void Model::rebuildShapes() { } 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 and put it at the center of the model + // 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, _offset); + SphereShape* sphere = new SphereShape(0.f, worldPosition); _jointShapes.push_back(sphere); } - } // bounding shape @@ -554,7 +578,12 @@ void Model::rebuildShapes() { 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() { @@ -581,6 +610,7 @@ void Model::updateShapePositions() { _boundingRadius = sqrtf(_boundingRadius); _shapesAreDirty = false; _boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); } }