Merge branch 'master' of https://github.com/highfidelity/hifi into decouple-avatar-updates

This commit is contained in:
Howard Stearns 2015-09-09 10:37:11 -07:00
commit 73a5714d66
19 changed files with 297 additions and 199 deletions

View file

@ -14,7 +14,7 @@ Script.load("selectAudioDevice.js");
Script.load("inspect.js");
Script.load("notifications.js");
Script.load("users.js");
Script.load("handControllerGrab.js");
Script.load("controllers/handControllerGrab.js");
Script.load("grab.js");
Script.load("directory.js");
Script.load("dialTone.js");

View file

@ -71,7 +71,7 @@
if (_this.injector == null) {
_this.injector = Audio.playSound(_this.song, {
position: props.position, // position of boombox entity
volume: 0.5,
volume: 0.1,
loop: true
});
} else {

View file

@ -96,20 +96,44 @@ mergeObjects = function(proto, custom) {
return result;
}
LOG_WARN = 1;
logWarn = function(str) {
print(str);
if (LOG_WARN) {
print(str);
}
}
LOG_ERROR = 1;
logError = function(str) {
print(str);
if (LOG_ERROR) {
print(str);
}
}
LOG_INFO = 1;
logInfo = function(str) {
print(str);
if (LOG_INFO) {
print(str);
}
}
LOG_DEBUG = 0;
logDebug = function(str) {
print(str);
if (LOG_DEBUG) {
print(str);
}
}
LOG_TRACE = 0;
logTrace = function(str) {
if (LOG_TRACE) {
print(str);
}
}
// Computes the penetration between a point and a sphere (centered at the origin)

View file

@ -26,7 +26,7 @@
this.collisionWithEntity = function(myID, otherID, collision) {
//if(Entites.getEntityProperties(otherID).userData.objectType==='') { merge bubbles?}
Entities.deleteEntity(myID);
// Entities.deleteEntity(myID);
// this.burstBubbleSound(collision.contactPoint)
};

View file

@ -35,7 +35,7 @@ var wand = Entities.addEntity({
});
function cleanup() {
//Entities.deleteEntity(wand);
Entities.deleteEntity(wand);
}

View file

@ -761,7 +761,7 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) {
_enableDebugDrawAnimPose = isEnabled;
if (!isEnabled) {
AnimDebugDraw::getInstance().removeAnimNode("myAvatar");
AnimDebugDraw::getInstance().removePoses("myAvatar");
}
}
@ -1274,8 +1274,6 @@ void MyAvatar::initAnimGraph() {
void MyAvatar::destroyAnimGraph() {
_rig->destroyAnimGraph();
AnimDebugDraw::getInstance().removeSkeleton("myAvatar");
AnimDebugDraw::getInstance().removeAnimNode("myAvatar");
}
void MyAvatar::preRender(RenderArgs* renderArgs) {
@ -1287,26 +1285,35 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
initHeadBones();
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
initAnimGraph();
_debugDrawSkeleton = std::make_shared<AnimSkeleton>(_skeletonModel.getGeometry()->getFBXGeometry());
}
if (_enableDebugDrawBindPose || _enableDebugDrawAnimPose) {
AnimSkeleton::ConstPointer animSkeleton = _rig->getAnimSkeleton();
AnimNode::ConstPointer animNode = _rig->getAnimNode();
// bones space is rotated
// bones are aligned such that z is forward, not -z.
glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f));
AnimPose xform(glm::vec3(1), rotY180 * getOrientation(), getPosition());
if (animSkeleton && _enableDebugDrawBindPose) {
if (_enableDebugDrawBindPose && _debugDrawSkeleton) {
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
AnimDebugDraw::getInstance().addSkeleton("myAvatar", animSkeleton, xform, gray);
AnimDebugDraw::getInstance().addSkeleton("myAvatar", _debugDrawSkeleton, xform, gray);
}
// This only works for when new anim system is enabled, at the moment.
if (animNode && animSkeleton && _enableDebugDrawAnimPose && _rig->getEnableAnimGraph()) {
if (_enableDebugDrawAnimPose && _debugDrawSkeleton) {
glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f);
AnimDebugDraw::getInstance().addAnimNode("myAvatar", animNode, xform, cyan);
// build AnimPoseVec from JointStates.
AnimPoseVec poses;
poses.reserve(_debugDrawSkeleton->getNumJoints());
for (int i = 0; i < _debugDrawSkeleton->getNumJoints(); i++) {
AnimPose pose = _debugDrawSkeleton->getRelativeBindPose(i);
glm::quat jointRot;
_rig->getJointRotationInConstrainedFrame(i, jointRot);
pose.rot = pose.rot * jointRot;
poses.push_back(pose);
}
AnimDebugDraw::getInstance().addPoses("myAvatar", _debugDrawSkeleton, poses, xform, cyan);
}
}

View file

@ -323,6 +323,7 @@ private:
bool _enableDebugDrawBindPose = false;
bool _enableDebugDrawAnimPose = false;
AnimSkeleton::ConstPointer _debugDrawSkeleton = nullptr;
};
#endif // hifi_MyAvatar_h

View file

@ -103,13 +103,15 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
}
Model::updateRig(deltaTime, parentTransform);
Head* head = _owningAvatar->getHead();
if (_owningAvatar->isMyAvatar()) {
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
Head* head = _owningAvatar->getHead();
Rig::HeadParameters params;
params.modelRotation = getRotation();
params.modelTranslation = getTranslation();
params.enableLean = qApp->isHMDMode() && !myAvatar->getStandingHMDSensorMode();
params.leanSideways = head->getFinalLeanSideways();
params.leanForward = head->getFinalLeanForward();
params.torsoTwist = head->getTorsoTwist();
@ -133,7 +135,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now.
// We will revisit that as priorities allow, and particularly after the new rig/animation/joints.
const FBXGeometry& geometry = _geometry->getFBXGeometry();
Head* head = _owningAvatar->getHead();
// If the head is not positioned, updateEyeJoints won't get the math right
glm::quat headOrientation;
_rig->getJointRotation(geometry.headJointIndex, headOrientation);
@ -548,14 +549,24 @@ 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);
}
// HACK so that default legless robot doesn't knuckle-drag
if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) {
totalExtents.addPoint(extractTranslation(jointTransform));
}
}
// compute bounding shape parameters

View file

@ -58,7 +58,52 @@ AnimPose::operator glm::mat4() const {
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
}
AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) {
// convert to std::vector of joints
std::vector<FBXJoint> joints;
joints.reserve(fbxGeometry.joints.size());
for (auto& joint : fbxGeometry.joints) {
joints.push_back(joint);
}
AnimPose geometryOffset(fbxGeometry.offset);
buildSkeletonFromJoints(joints, geometryOffset);
}
AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset) {
buildSkeletonFromJoints(joints, geometryOffset);
}
int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
for (size_t i = 0; i < _joints.size(); i++) {
if (_joints[i].name == jointName) {
return i;
}
}
return -1;
}
int AnimSkeleton::getNumJoints() const {
return _joints.size();
}
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
return _absoluteBindPoses[jointIndex];
}
const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const {
return _relativeBindPoses[jointIndex];
}
int AnimSkeleton::getParentIndex(int jointIndex) const {
return _joints[jointIndex].parentIndex;
}
const QString& AnimSkeleton::getJointName(int jointIndex) const {
return _joints[jointIndex].name;
}
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset) {
_joints = joints;
// build a cache of bind poses
@ -113,35 +158,6 @@ AnimSkeleton::AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose&
}
}
int AnimSkeleton::nameToJointIndex(const QString& jointName) const {
for (size_t i = 0; i < _joints.size(); i++) {
if (_joints[i].name == jointName) {
return i;
}
}
return -1;
}
int AnimSkeleton::getNumJoints() const {
return _joints.size();
}
AnimPose AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
return _absoluteBindPoses[jointIndex];
}
AnimPose AnimSkeleton::getRelativeBindPose(int jointIndex) const {
return _relativeBindPoses[jointIndex];
}
int AnimSkeleton::getParentIndex(int jointIndex) const {
return _joints[jointIndex].parentIndex;
}
const QString& AnimSkeleton::getJointName(int jointIndex) const {
return _joints[jointIndex].name;
}
#ifndef NDEBUG
void AnimSkeleton::dump() const {
qCDebug(animation) << "[";

View file

@ -47,16 +47,17 @@ public:
using Pointer = std::shared_ptr<AnimSkeleton>;
using ConstPointer = std::shared_ptr<const AnimSkeleton>;
AnimSkeleton(const FBXGeometry& fbxGeometry);
AnimSkeleton(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset);
int nameToJointIndex(const QString& jointName) const;
const QString& getJointName(int jointIndex) const;
int getNumJoints() const;
// absolute pose, not relative to parent
AnimPose getAbsoluteBindPose(int jointIndex) const;
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
// relative to parent pose
AnimPose getRelativeBindPose(int jointIndex) const;
const AnimPose& getRelativeBindPose(int jointIndex) const;
int getParentIndex(int jointIndex) const;
@ -66,6 +67,8 @@ public:
#endif
protected:
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints, const AnimPose& geometryOffset);
std::vector<FBXJoint> _joints;
AnimPoseVec _absoluteBindPoses;
AnimPoseVec _relativeBindPoses;

View file

@ -41,7 +41,6 @@ void JointState::copyState(const JointState& other) {
// DO NOT copy _constraint
_name = other._name;
_isFree = other._isFree;
_boneRadius = other._boneRadius;
_parentIndex = other._parentIndex;
_defaultRotation = other._defaultRotation;
_inverseDefaultRotation = other._inverseDefaultRotation;
@ -58,7 +57,6 @@ JointState::JointState(const FBXJoint& joint) {
_rotationInConstrainedFrame = joint.rotation;
_name = joint.name;
_isFree = joint.isFree;
_boneRadius = joint.boneRadius;
_parentIndex = joint.parentIndex;
_translation = joint.translation;
_defaultRotation = joint.rotation;

View file

@ -118,7 +118,6 @@ 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; }
bool getIsFree() const { return _isFree; }
float getAnimationPriority() const { return _animationPriority; }
void setAnimationPriority(float priority) { _animationPriority = priority; }
@ -149,7 +148,6 @@ private:
QString _name;
int _parentIndex;
bool _isFree;
float _boneRadius;
glm::vec3 _rotationMin;
glm::vec3 _rotationMax;
glm::quat _preRotation;

View file

@ -477,7 +477,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
}
if (glm::length(localVel) > moveThresh) {
if (fabs(forwardSpeed) > 0.5f * fabs(lateralSpeed)) {
if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) {
if (forwardSpeed > 0.0f) {
// forward
_animVars.set("isMovingForward", true);
@ -501,7 +501,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
}
_state = RigRole::Move;
} else {
if (fabs(turningSpeed) > turnThresh) {
if (fabsf(turningSpeed) > turnThresh) {
if (turningSpeed > 0.0f) {
// turning right
_animVars.set("isTurningRight", true);
@ -901,19 +901,20 @@ glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targ
return endRotation;
}
bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
quatOut = _jointStates[jointIndex].getRotationInConstrainedFrame();
return true;
}
void Rig::updateVisibleJointStates() {
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
}
void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
}
_jointStates[jointIndex].setTransform(newTransform);
}
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
@ -936,7 +937,9 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
}
void Rig::updateFromHeadParameters(const HeadParameters& params) {
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
if (params.enableLean) {
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
}
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
@ -1010,16 +1013,7 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
return;
}
// convert to std::vector of joints
std::vector<FBXJoint> joints;
joints.reserve(fbxGeometry.joints.size());
for (auto& joint : fbxGeometry.joints) {
joints.push_back(joint);
}
// create skeleton
AnimPose geometryOffset(fbxGeometry.offset);
_animSkeleton = std::make_shared<AnimSkeleton>(joints, geometryOffset);
_animSkeleton = std::make_shared<AnimSkeleton>(fbxGeometry);
// load the anim graph
_animLoader.reset(new AnimNodeLoader(url));

View file

@ -50,13 +50,13 @@ class Rig;
typedef std::shared_ptr<Rig> RigPointer;
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
public:
struct HeadParameters {
float leanSideways = 0.0f; // degrees
float leanForward = 0.0f; // degrees
float torsoTwist = 0.0f; // degrees
bool enableLean = false;
glm::quat modelRotation = glm::quat();
glm::quat localHeadOrientation = glm::quat();
glm::quat worldHeadOrientation = glm::quat();
@ -133,7 +133,6 @@ public:
glm::vec3 translation, glm::quat rotation) const;
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const;
glm::mat4 getJointTransform(int jointIndex) const;
void setJointTransform(int jointIndex, glm::mat4 newTransform);
glm::mat4 getJointVisibleTransform(int jointIndex) const;
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
// Start or stop animations as needed.
@ -153,6 +152,7 @@ public:
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
float priority, bool constrain = false, float mix = 1.0f);
bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const;
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
void updateVisibleJointStates();

View file

@ -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());

View file

@ -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

View file

@ -441,7 +441,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, 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);
@ -617,7 +616,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;

View file

@ -152,22 +152,30 @@ AnimDebugDraw::~AnimDebugDraw() {
}
}
void AnimDebugDraw::addSkeleton(std::string key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color) {
void AnimDebugDraw::addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color) {
_skeletons[key] = SkeletonInfo(skeleton, rootPose, color);
}
void AnimDebugDraw::removeSkeleton(std::string key) {
void AnimDebugDraw::removeSkeleton(const std::string& key) {
_skeletons.erase(key);
}
void AnimDebugDraw::addAnimNode(std::string key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color) {
void AnimDebugDraw::addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color) {
_animNodes[key] = AnimNodeInfo(animNode, rootPose, color);
}
void AnimDebugDraw::removeAnimNode(std::string key) {
void AnimDebugDraw::removeAnimNode(const std::string& key) {
_animNodes.erase(key);
}
void AnimDebugDraw::addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) {
_poses[key] = PosesInfo(skeleton, poses, rootPose, color);
}
void AnimDebugDraw::removePoses(const std::string& key) {
_poses.erase(key);
}
static const uint32_t red = toRGBA(255, 0, 0, 255);
static const uint32_t green = toRGBA(0, 255, 0, 255);
static const uint32_t blue = toRGBA(0, 0, 255, 255);
@ -176,6 +184,8 @@ const int NUM_CIRCLE_SLICES = 24;
static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, Vertex*& v) {
const float XYZ_AXIS_LENGTH = radius * 4.0f;
AnimPose finalPose = rootPose * pose;
glm::vec3 base = rootPose * pose.trans;
@ -195,7 +205,7 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
v->pos = base;
v->rgba = red;
v++;
v->pos = finalPose * glm::vec3(radius * 2.0f, 0.0f, 0.0f);
v->pos = finalPose * glm::vec3(XYZ_AXIS_LENGTH, 0.0f, 0.0f);
v->rgba = red;
v++;
@ -213,7 +223,7 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
v->pos = base;
v->rgba = green;
v++;
v->pos = finalPose * glm::vec3(0.0f, radius * 2.0f, 0.0f);
v->pos = finalPose * glm::vec3(0.0f, XYZ_AXIS_LENGTH, 0.0f);
v->rgba = green;
v++;
@ -231,7 +241,7 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
v->pos = base;
v->rgba = blue;
v++;
v->pos = finalPose * glm::vec3(0.0f, 0.0f, radius * 2.0f);
v->pos = finalPose * glm::vec3(0.0f, 0.0f, XYZ_AXIS_LENGTH);
v->rgba = blue;
v++;
@ -322,11 +332,11 @@ void AnimDebugDraw::update() {
const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3);
const size_t VERTICES_PER_LINK = 8 * 2;
const float BONE_RADIUS = 0.0075f;
const float BONE_RADIUS = 0.01f; // 1 cm
// figure out how many verts we will need.
int numVerts = 0;
for (auto&& iter : _skeletons) {
for (auto& iter : _skeletons) {
AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second);
numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE;
for (int i = 0; i < skeleton->getNumJoints(); i++) {
@ -337,7 +347,7 @@ void AnimDebugDraw::update() {
}
}
for (auto&& iter : _animNodes) {
for (auto& iter : _animNodes) {
AnimNode::ConstPointer& animNode = std::get<0>(iter.second);
auto poses = animNode->getPosesInternal();
numVerts += poses.size() * VERTICES_PER_BONE;
@ -350,10 +360,21 @@ void AnimDebugDraw::update() {
}
}
for (auto& iter : _poses) {
AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second);
numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE;
for (int i = 0; i < skeleton->getNumJoints(); i++) {
auto parentIndex = skeleton->getParentIndex(i);
if (parentIndex >= 0) {
numVerts += VERTICES_PER_LINK;
}
}
}
data._vertexBuffer->resize(sizeof(Vertex) * numVerts);
Vertex* verts = (Vertex*)data._vertexBuffer->editData();
Vertex* v = verts;
for (auto&& iter : _skeletons) {
for (auto& iter : _skeletons) {
AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second);
AnimPose rootPose = std::get<1>(iter.second);
int hipsIndex = skeleton->nameToJointIndex("Hips");
@ -380,7 +401,7 @@ void AnimDebugDraw::update() {
}
}
for (auto&& iter : _animNodes) {
for (auto& iter : _animNodes) {
AnimNode::ConstPointer& animNode = std::get<0>(iter.second);
AnimPose rootPose = std::get<1>(iter.second);
if (animNode->_skeleton) {
@ -418,6 +439,42 @@ void AnimDebugDraw::update() {
}
}
for (auto& iter : _poses) {
AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second);
AnimPoseVec& poses = std::get<1>(iter.second);
AnimPose rootPose = std::get<2>(iter.second);
int hipsIndex = skeleton->nameToJointIndex("Hips");
if (hipsIndex >= 0) {
rootPose.trans -= skeleton->getRelativeBindPose(hipsIndex).trans;
}
glm::vec4 color = std::get<3>(iter.second);
std::vector<AnimPose> absAnimPose;
absAnimPose.resize(skeleton->getNumJoints());
for (int i = 0; i < skeleton->getNumJoints(); i++) {
const AnimPose& pose = poses[i];
const float radius = BONE_RADIUS / (pose.scale.x * rootPose.scale.x);
auto parentIndex = skeleton->getParentIndex(i);
if (parentIndex >= 0) {
absAnimPose[i] = absAnimPose[parentIndex] * pose;
} else {
absAnimPose[i] = pose;
}
// draw bone
addBone(rootPose, absAnimPose[i], radius, v);
// draw link to parent
if (parentIndex >= 0) {
assert(parentIndex < skeleton->getNumJoints());
addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, color, v);
}
}
}
assert(numVerts == (v - verts));
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);

View file

@ -27,11 +27,14 @@ public:
AnimDebugDraw();
~AnimDebugDraw();
void addSkeleton(std::string key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color);
void removeSkeleton(std::string key);
void addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color);
void removeSkeleton(const std::string& key);
void addAnimNode(std::string key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color);
void removeAnimNode(std::string key);
void addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color);
void removeAnimNode(const std::string& key);
void addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color);
void removePoses(const std::string& key);
void update();
@ -44,9 +47,11 @@ protected:
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPose, glm::vec4> SkeletonInfo;
typedef std::tuple<AnimNode::ConstPointer, AnimPose, glm::vec4> AnimNodeInfo;
typedef std::tuple<AnimSkeleton::ConstPointer, AnimPoseVec, AnimPose, glm::vec4> PosesInfo;
std::unordered_map<std::string, SkeletonInfo> _skeletons;
std::unordered_map<std::string, AnimNodeInfo> _animNodes;
std::unordered_map<std::string, PosesInfo> _poses;
// no copies
AnimDebugDraw(const AnimDebugDraw&) = delete;