More work on custom skeletons.

This commit is contained in:
Andrzej Kapolka 2013-10-21 16:14:38 -07:00
parent 49acca3587
commit 349f7b363a
8 changed files with 149 additions and 57 deletions

View file

@ -6,6 +6,8 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <glm/gtx/transform.hpp>
#include "Avatar.h"
#include "FaceModel.h"
#include "Head.h"
@ -34,22 +36,37 @@ void FaceModel::simulate(float deltaTime) {
Model::simulate(deltaTime);
}
void FaceModel::maybeUpdateNeckRotation(const FBXJoint& joint, JointState& state) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(getRotation());
glm::mat3 inverse = glm::inverse(glm::mat3(_jointStates[joint.parentIndex].transform *
joint.preRotation * glm::mat4_cast(joint.rotation)));
state.rotation = glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
}
void FaceModel::maybeUpdateEyeRotation(const FBXJoint& joint, JointState& state) {
// get the lookat position in joint space and use it to adjust the rotation
glm::mat4 inverse = glm::inverse(_jointStates[joint.parentIndex].transform *
joint.preRotation * glm::mat4_cast(joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
_owningHead->getSaccade(), 1.0f));
state.rotation = rotationBetween(front, lookAt) * joint.rotation;
void FaceModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(index);
if (joint.parentIndex == -1) {
glm::mat4 baseTransform = glm::translate(_translation) * glm::mat4_cast(_rotation) *
glm::scale(_scale) * glm::translate(_offset);
state.transform = baseTransform * geometry.offset * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
} else {
if (index == geometry.neckJointIndex) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(getRotation());
glm::mat3 inverse = glm::inverse(glm::mat3(_jointStates[joint.parentIndex].transform *
joint.preRotation * glm::mat4_cast(joint.rotation)));
state.rotation = glm::angleAxis(_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
// likewise with the eye joints
glm::mat4 inverse = glm::inverse(_jointStates[joint.parentIndex].transform *
joint.preRotation * glm::mat4_cast(joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
_owningHead->getSaccade(), 1.0f));
state.rotation = rotationBetween(front, lookAt) * joint.rotation;
}
state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
}
}

View file

@ -25,11 +25,8 @@ public:
protected:
/// Applies neck rotation based on head orientation.
virtual void maybeUpdateNeckRotation(const FBXJoint& joint, JointState& state);
/// Applies eye rotation based on lookat position.
virtual void maybeUpdateEyeRotation(const FBXJoint& joint, JointState& state);
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
private:

View file

@ -6,6 +6,8 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <glm/gtx/transform.hpp>
#include "Avatar.h"
#include "SkeletonModel.h"
@ -26,3 +28,62 @@ void SkeletonModel::simulate(float deltaTime) {
Model::simulate(deltaTime);
}
bool SkeletonModel::render(float alpha) {
if (_jointStates.isEmpty()) {
return false;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
const JointState& joint = _jointStates.at(i);
glPushMatrix();
glMultMatrixf((const GLfloat*)&joint.transform);
if (i == geometry.rootJointIndex) {
glColor3f(1.0f, 0.0f, 0.0f);
} else {
glColor3f(1.0f, 1.0f, 1.0f);
}
glutSolidSphere(0.05f, 10, 10);
glPopMatrix();
}
Model::render(alpha);
return true;
}
void SkeletonModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(index);
if (joint.parentIndex == -1) {
glm::mat4 baseTransform = glm::translate(_translation) * glm::mat4_cast(_rotation) *
glm::scale(_scale) * glm::translate(_offset);
state.transform = baseTransform * geometry.offset * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
} else {
if (index == geometry.leanJointIndex) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::inverse(glm::mat3(_jointStates[joint.parentIndex].transform *
joint.preRotation * glm::mat4_cast(joint.rotation)));
state.rotation = glm::angleAxis(_owningAvatar->getHead().getLeanSideways(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningAvatar->getHead().getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
}
state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
}
if (index == geometry.rootJointIndex) {
state.transform[3][0] = _translation.x;
state.transform[3][1] = _translation.y;
state.transform[3][2] = _translation.z;
}
}

View file

@ -22,6 +22,12 @@ public:
SkeletonModel(Avatar* owningAvatar);
void simulate(float deltaTime);
bool render(float alpha);
protected:
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
private:

View file

@ -660,9 +660,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
QByteArray jointEyeLeftName = joints.value("jointEyeLeft", "jointEyeLeft").toByteArray();
QByteArray jointEyeRightName = joints.value("jointEyeRight", "jointEyeRight").toByteArray();
QByteArray jointNeckName = joints.value("jointNeck", "jointNeck").toByteArray();
QByteArray jointRootName = joints.value("jointRoot", "jointRoot").toByteArray();
QByteArray jointLeanName = joints.value("jointLean", "jointLean").toByteArray();
QString jointEyeLeftID;
QString jointEyeRightID;
QString jointNeckID;
QString jointRootID;
QString jointLeanID;
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
QHash<QByteArray, QPair<int, float> > blendshapeIndices;
@ -711,8 +715,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
blendshapes.append(extracted);
}
} else if (object.name == "Model") {
QByteArray name = object.properties.at(1).toByteArray();
name = name.left(name.indexOf('\0'));
QByteArray name;
if (object.properties.size() == 3) {
name = object.properties.at(1).toByteArray();
name = name.left(name.indexOf('\0'));
} else {
name = object.properties.at(0).toByteArray();
}
if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") {
jointEyeLeftID = object.properties.at(0).toString();
@ -721,6 +731,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") {
jointNeckID = object.properties.at(0).toString();
} else if (name == jointRootName) {
jointRootID = object.properties.at(0).toString();
} else if (name == jointLeanName) {
jointLeanID = object.properties.at(0).toString();
}
glm::vec3 translation;
glm::vec3 rotationOffset;
@ -947,6 +963,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID);
geometry.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID);
geometry.neckJointIndex = modelIDs.indexOf(jointNeckID);
geometry.rootJointIndex = modelIDs.indexOf(jointRootID);
geometry.leanJointIndex = modelIDs.indexOf(jointLeanID);
// extract the translation component of the neck transform
if (geometry.neckJointIndex != -1) {
@ -973,7 +991,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
FBXMeshPart& part = mesh.parts[partIndex];
if (textureFilenames.contains(childID)) {
part.diffuseFilename = textureFilenames.value(childID);
qDebug() << "hello " << part.diffuseFilename << "\n";
continue;
}
if (!materials.contains(childID)) {

View file

@ -109,6 +109,8 @@ public:
int leftEyeJointIndex;
int rightEyeJointIndex;
int neckJointIndex;
int rootJointIndex;
int leanJointIndex;
glm::vec3 neckPivot;
};

View file

@ -91,22 +91,7 @@ void Model::simulate(float deltaTime) {
// update the world space transforms for all joints
for (int i = 0; i < _jointStates.size(); i++) {
JointState& state = _jointStates[i];
const FBXJoint& joint = geometry.joints.at(i);
if (joint.parentIndex == -1) {
state.transform = baseTransform * geometry.offset * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
} else {
if (i == geometry.neckJointIndex) {
maybeUpdateNeckRotation(joint, state);
} else if (i == geometry.leftEyeJointIndex || i == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(joint, state);
}
state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
}
updateJointState(i);
}
for (int i = 0; i < _meshStates.size(); i++) {
@ -396,12 +381,21 @@ glm::vec4 Model::computeAverageColor() const {
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
void Model::maybeUpdateNeckRotation(const FBXJoint& joint, JointState& state) {
// nothing by default
}
void Model::maybeUpdateEyeRotation(const FBXJoint& joint, JointState& state) {
// nothing by default
void Model::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXJoint& joint = geometry.joints.at(index);
if (joint.parentIndex == -1) {
glm::mat4 baseTransform = glm::translate(_translation) * glm::mat4_cast(_rotation) *
glm::scale(_scale) * glm::translate(_offset);
state.transform = baseTransform * geometry.offset * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
} else {
state.transform = _jointStates[joint.parentIndex].transform * joint.preRotation *
glm::mat4_cast(state.rotation) * joint.postRotation;
}
}
void Model::deleteGeometry() {

View file

@ -65,6 +65,11 @@ protected:
QSharedPointer<NetworkGeometry> _geometry;
glm::vec3 _translation;
glm::quat _rotation;
glm::vec3 _scale;
glm::vec3 _offset;
class JointState {
public:
glm::quat rotation;
@ -72,21 +77,14 @@ protected:
};
QVector<JointState> _jointStates;
/// Gives subclasses a chance to update the neck joint rotation after its parents and before its children.
virtual void maybeUpdateNeckRotation(const FBXJoint& joint, JointState& state);
/// Gives subclasses a chance to update an eye joint rotation after its parents and before its children.
virtual void maybeUpdateEyeRotation(const FBXJoint& joint, JointState& state);
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
private:
void deleteGeometry();
glm::vec3 _translation;
glm::quat _rotation;
glm::vec3 _scale;
glm::vec3 _offset;
float _pupilDilation;
std::vector<float> _blendshapeCoefficients;