Merge branch 'rig' of https://github.com/sethalves/hifi into sethalves-rig

This commit is contained in:
Howard Stearns 2015-07-22 21:08:57 -07:00
commit a1a10ad08e
19 changed files with 564 additions and 637 deletions

View file

@ -45,6 +45,7 @@
#include "Util.h"
#include "world.h"
#include "InterfaceLogging.h"
#include "EntityRig.h"
using namespace std;
@ -965,7 +966,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (_unusedAttachments.size() > 0) {
model = _unusedAttachments.takeFirst();
} else {
model = new Model(this);
model = new Model(std::make_shared<EntityRig>(), this);
}
model->init();
_attachmentModels.append(model);

View file

@ -35,6 +35,7 @@
#include "Menu.h"
#include "MyAvatar.h"
#include "SceneScriptingInterface.h"
#include "AvatarRig.h"
// 70 times per second - target is 60hz, but this helps account for any small deviations
// in the update loop
@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) :
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = std::make_shared<MyAvatar>(std::make_shared<Rig>());
_myAvatar = std::make_shared<MyAvatar>(std::make_shared<AvatarRig>());
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
AvatarSharedPointer AvatarManager::newSharedAvatar() {
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<Rig>()));
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<AvatarRig>()));
}
// virtual

View file

@ -16,9 +16,11 @@
#include "Head.h"
#include "Menu.h"
FaceModel::FaceModel(Head* owningHead) :
FaceModel::FaceModel(Head* owningHead, RigPointer rig) :
Model(rig, nullptr),
_owningHead(owningHead)
{
assert(_rig);
}
void FaceModel::simulate(float deltaTime, bool fullUpdate) {
@ -48,54 +50,58 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
}
}
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
_owningHead->getTorsoTwist(),
_owningHead->getFinalLeanSideways()));
pitchYawRoll -= lean;
state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
* joint.rotation, DEFAULT_PRIORITY);
_rig->setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
* joint.rotation, DEFAULT_PRIORITY);
}
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) {
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) {
// likewise with the eye joints
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() +
_owningHead->getSaccade() - model->getTranslation(), 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation, DEFAULT_PRIORITY);
_rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
-MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation, DEFAULT_PRIORITY);
}
void FaceModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const JointState& state = _rig->getJointState(index);
const FBXJoint& joint = state.getFBXJoint();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) {
const JointState& parentState = _rig->getJointState(joint.parentIndex);
if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, state);
maybeUpdateNeckRotation(parentState, joint, index);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(this, parentState, joint, state);
maybeUpdateEyeRotation(this, parentState, joint, index);
}
}
Model::updateJointState(index);
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->updateFaceJointState(index, parentTransform);
}
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {

View file

@ -22,12 +22,12 @@ class FaceModel : public Model {
public:
FaceModel(Head* owningHead);
FaceModel(Head* owningHead, RigPointer rig);
virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index);
virtual void updateJointState(int index);
/// Retrieve the positions of up to two eye meshes.

View file

@ -23,6 +23,7 @@
#include "Util.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "AvatarRig.h"
using namespace std;
@ -55,11 +56,10 @@ Head::Head(Avatar* owningAvatar) :
_deltaLeanForward(0.0f),
_isCameraMoving(false),
_isLookingAtMe(false),
_faceModel(this),
_faceModel(this, std::make_shared<AvatarRig>()),
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
{
}
void Head::init() {

View file

@ -30,7 +30,7 @@ enum StandingFootState {
};
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
Model(parent, rig),
Model(rig, parent),
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
_owningAvatar(owningAvatar),
_boundingShape(),
@ -51,11 +51,13 @@ SkeletonModel::~SkeletonModel() {
}
void SkeletonModel::initJointStates(QVector<JointState> states) {
Model::initJointStates(states);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_boundingRadius = _rig->initJointStates(states, parentTransform);
// Determine the default eye position for avatar scale = 1.0
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
if (0 <= headJointIndex && headJointIndex < _jointStates.size()) {
if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) {
glm::vec3 leftEyePosition, rightEyePosition;
getEyeModelPositions(leftEyePosition, rightEyePosition);
@ -75,8 +77,8 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
// the SkeletonModel override of updateJointState() will clear the translation part
// of its root joint and we need that done before we try to build shapes hence we
// recompute all joint transforms at this time.
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
for (int i = 0; i < _rig->getJointStateCount(); i++) {
_rig->updateJointState(i, parentTransform);
}
clearShapes();
@ -168,7 +170,7 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes)
|| jointIndex == getRightHandJointIndex()) {
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
for (int i = 0; i < _rig->getJointStateCount(); i++) {
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
Shape* shape = _shapes[i];
@ -211,7 +213,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
}
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
// NOTE: 'position' is in model-frame
@ -226,16 +228,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
if (forearmLength < EPSILON) {
return;
}
JointState& state = _jointStates[jointIndex];
glm::quat handRotation = state.getRotation();
glm::quat handRotation;
if (!_rig->getJointStateRotation(jointIndex, handRotation)) {
return;
}
// align hand with forearm
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY);
_rig->applyJointRotationDelta(jointIndex,
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
true, PALM_PRIORITY);
}
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
@ -261,43 +267,40 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palmPosition + forearm,
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
JointState& parentState = _jointStates[parentJointIndex];
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY);
_rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY);
} else {
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
}
}
void SkeletonModel::updateJointState(int index) {
if (index < 0 && index >= _jointStates.size()) {
return; // bail
}
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
const JointState joint = _rig->getJointState(index);
if (joint.getParentIndex() >= 0 && joint.getParentIndex() < _rig->getJointStateCount()) {
const JointState parentState = _rig->getJointState(joint.getParentIndex());
if (index == geometry.leanJointIndex) {
maybeUpdateLeanRotation(parentState, state);
maybeUpdateLeanRotation(parentState, index);
} else if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, state);
maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(parentState, joint, state);
maybeUpdateEyeRotation(parentState, joint.getFBXJoint(), index);
}
}
Model::updateJointState(index);
_rig->updateJointState(index, parentTransform);
if (index == _geometry->getFBXGeometry().rootJointIndex) {
state.clearTransformTranslation();
_rig->clearJointTransformTranslation(index);
}
}
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) {
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, int index) {
if (!_owningAvatar->isMyAvatar()) {
return;
}
@ -305,24 +308,24 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, Joint
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
state.setRotationInConstrainedFrame(
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
* state.getFBXJoint().rotation, LEAN_PRIORITY);
glm::quat inverse = glm::inverse(parentState.getRotation() * _rig->getJointDefaultRotationInParentFrame(index));
_rig->setJointRotationInConstrainedFrame(index,
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
* _rig->getJointState(index).getFBXJoint().rotation, LEAN_PRIORITY);
}
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state);
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index);
}
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state);
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, index);
}
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
@ -331,9 +334,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
batch._glLineWidth(3.0f);
do {
const FBXJoint& joint = geometry.joints.at(jointIndex);
const JointState& jointState = _jointStates.at(jointIndex);
const JointState& jointState = _rig->getJointState(jointIndex);
glm::vec3 position = _rotation * jointState.getPosition() + _translation;
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation();
glm::quat parentRotation = (joint.parentIndex == -1) ?
_rotation :
_rotation * _rig->getJointState(joint.parentIndex).getRotation();
float fanScale = directionSize * 0.75f;
Transform transform = Transform();
@ -464,14 +469,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
JointState& shoulderState = _jointStates[shoulderJointIndex];
shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY);
JointState& elbowState = _jointStates[elbowJointIndex];
elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
JointState& handState = _jointStates[jointIndex];
handState.setRotationInBindFrame(rotation, PALM_PRIORITY);
_rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
_rig->setJointRotationInBindFrame(elbowJointIndex,
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
shoulderRotation, PALM_PRIORITY);
_rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY);
}
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
@ -526,7 +528,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP
glm::quat worldFrameRotation;
bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
if (success) {
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation;
}
return success;
}
@ -636,7 +638,7 @@ float VERY_BIG_MASS = 1.0e6f;
// virtual
void SkeletonModel::buildShapes() {
if (_geometry == NULL || _jointStates.isEmpty()) {
if (_geometry == NULL || _rig->jointStatesEmpty()) {
return;
}
@ -647,9 +649,8 @@ void SkeletonModel::buildShapes() {
}
float uniformScale = extractUniformScale(_scale);
const int numStates = _jointStates.size();
for (int i = 0; i < numStates; i++) {
JointState& state = _jointStates[i];
for (int i = 0; i < _rig->getJointStateCount(); i++) {
const JointState& state = _rig->getJointState(i);
const FBXJoint& joint = state.getFBXJoint();
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
@ -683,7 +684,7 @@ void SkeletonModel::buildShapes() {
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
// compute default joint transforms
int numStates = _jointStates.size();
int numStates = _rig->getJointStateCount();
assert(numStates == _shapes.size());
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numStates);
@ -694,11 +695,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
totalExtents.addPoint(glm::vec3(0.0f));
for (int i = 0; i < numStates; i++) {
// compute the default transform of this joint
JointState& state = _jointStates[i];
const JointState& state = _rig->getJointState(i);
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
transforms[i] = _jointStates[i].getTransform();
transforms[i] = _rig->getJointTransform(i);
} else {
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
@ -748,7 +749,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition();
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
_boundingRadius = 0.5f * glm::length(diagonal);
}
@ -853,7 +854,7 @@ void SkeletonModel::cauterizeHead() {
if (isActive()) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const int neckJointIndex = geometry.neckJointIndex;
if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) {
if (neckJointIndex > 0 && neckJointIndex < _rig->getJointStateCount()) {
// lazy init of headBones
if (_headBones.size() == 0) {
@ -861,13 +862,13 @@ void SkeletonModel::cauterizeHead() {
}
// preserve the translation for the neck
glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3];
// glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3];
glm::vec4 trans = _rig->getJointTransform(neckJointIndex)[3];
glm::vec4 zero(0, 0, 0, 0);
for (const int &i : _headBones) {
JointState& joint = _jointStates[i];
glm::mat4 newXform(zero, zero, zero, trans);
joint.setTransform(newXform);
joint.setVisibleTransform(newXform);
_rig->setJointTransform(i, newXform);
_rig->setJointVisibleTransform(i, newXform);
}
}
}

View file

@ -134,9 +134,9 @@ protected:
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
void maybeUpdateLeanRotation(const JointState& parentState, int index);
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index);
void cauterizeHead();
void initHeadBones();

View file

@ -14,7 +14,7 @@
#include "Application.h"
ModelOverlay::ModelOverlay()
: _model(),
: _model(nullptr),
_modelTextures(QVariantMap()),
_updateModel(false)
{
@ -24,7 +24,7 @@ ModelOverlay::ModelOverlay()
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
Volume3DOverlay(modelOverlay),
_model(),
_model(nullptr),
_modelTextures(QVariantMap()),
_url(modelOverlay->_url),
_updateModel(false)

View file

@ -159,15 +159,15 @@ void AnimationHandle::applyFrame(float frameIndex) {
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
float frameFraction = glm::fract(frameIndex);
QVector<JointState> jointStates = _rig->getJointStates();
assert(_rig->getJointStateCount() >= _jointMappings.size());
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
JointState& state = jointStates[mapping];
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i),
ceilFrame.rotations.at(i),
frameFraction),
_priority);
_rig->setJointRotationInConstrainedFrame(mapping,
safeMix(floorFrame.rotations.at(i),
ceilFrame.rotations.at(i),
frameFraction),
_priority);
}
}
}
@ -176,10 +176,9 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
QVector<JointState> jointStates = _rig->getJointStates();
JointState& state = jointStates[mapping];
JointState state = _rig->getJointState(mapping);
if (_priority == state._animationPriority) {
state._animationPriority = newPriority;
_rig->setJointAnimatinoPriority(mapping, newPriority);
}
}
}
@ -189,9 +188,8 @@ void AnimationHandle::restoreJoints() {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
QVector<JointState> jointStates = _rig->getJointStates();
JointState& state = jointStates[mapping];
state.restoreRotation(1.0f, state._animationPriority);
JointState state = _rig->getJointState(mapping);
_rig->restoreJointRotation(mapping, 1.0f, state._animationPriority);
}
}
}

View file

@ -0,0 +1,51 @@
//
// AvatarRig.cpp
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AvatarRig.h"
/// Updates the state of the joint at the specified index.
void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
if (index < 0 && index >= _jointStates.size()) {
return; // bail
}
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}
void AvatarRig::updateFaceJointState(int index, glm::mat4 parentTransform) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}

View file

@ -0,0 +1,28 @@
//
// AvatarRig.h
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AvatarRig_h
#define hifi_AvatarRig_h
#include <QObject>
#include "Rig.h"
class AvatarRig : public Rig {
Q_OBJECT
public:
~AvatarRig() {}
virtual void updateJointState(int index, glm::mat4 parentTransform);
virtual void updateFaceJointState(int index, glm::mat4 parentTransform);
};
#endif // hifi_AvatarRig_h

View file

@ -0,0 +1,30 @@
//
// EntityRig.cpp
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityRig.h"
/// Updates the state of the joint at the specified index.
void EntityRig::updateJointState(int index, glm::mat4 parentTransform) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}

View file

@ -0,0 +1,28 @@
//
// EntityRig.h
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_EntityRig_h
#define hifi_EntityRig_h
#include <QObject>
#include "Rig.h"
class EntityRig : public Rig {
Q_OBJECT
public:
~EntityRig() {}
virtual void updateJointState(int index, glm::mat4 parentTransform);
virtual void updateFaceJointState(int index, glm::mat4 parentTransform) { }
};
#endif // hifi_EntityRig_h

View file

@ -13,11 +13,7 @@
#include "Rig.h"
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.removeOne(animationHandle);
}
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
if (handle->getPriority() > (*it)->getPriority()) {
handles.insert(it, handle);
@ -27,17 +23,27 @@ static void insertSorted(QList<AnimationHandlePointer>& handles, const Animation
handles.append(handle);
}
AnimationHandlePointer Rig::createAnimationHandle() {
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
_animationHandles.insert(handle);
return handle;
}
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.removeOne(animationHandle);
}
void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
insertSorted(_runningAnimations, animationHandle);
insertSorted(_runningAnimations, animationHandle);
}
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.contains(animationHandle);
}
float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector<JointState> states) {
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform) {
_jointStates = states;
initJointTransforms(scale, offset);
initJointTransforms(parentTransform);
int numStates = _jointStates.size();
float radius = 0.0f;
@ -52,11 +58,11 @@ float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector<JointState
_jointStates[i].slaveVisibleTransform();
}
// XXX update AnimationHandles from here?
return radius;
}
void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) {
void Rig::initJointTransforms(glm::mat4 parentTransform) {
// compute model transforms
int numStates = _jointStates.size();
for (int i = 0; i < numStates; ++i) {
@ -64,9 +70,6 @@ void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) {
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
// NOTE: in practice geometry.offset has a non-unity scale (rather than a translation)
glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset); // * geometry.offset; XXX
state.initTransform(parentTransform);
} else {
const JointState& parentState = _jointStates.at(parentIndex);
@ -75,19 +78,30 @@ void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) {
}
}
void Rig::resetJoints() {
void Rig::clearJointTransformTranslation(int jointIndex) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
}
_jointStates[jointIndex].clearTransformTranslation();
}
void Rig::reset(const QVector<FBXJoint>& fbxJoints) {
if (_jointStates.isEmpty()) {
return;
}
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint();
_jointStates[i].setRotationInConstrainedFrame(fbxJoint.rotation, 0.0f);
_jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f);
}
}
bool Rig::getJointState(int index, glm::quat& rotation) const {
JointState Rig::getJointState(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return JointState();
}
return _jointStates[jointIndex];
}
bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
@ -105,12 +119,6 @@ bool Rig::getVisibleJointState(int index, glm::quat& rotation) const {
return !state.rotationIsDefault(rotation);
}
void Rig::updateVisibleJointStates() {
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
}
void Rig::clearJointState(int index) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
@ -122,6 +130,18 @@ void Rig::clearJointStates() {
_jointStates.clear();
}
void Rig::clearJointAnimationPriority(int index) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = 0.0f;
}
}
void Rig::setJointAnimatinoPriority(int index, float newPriority) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = newPriority;
}
}
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
@ -133,99 +153,103 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float
}
}
void Rig::clearJointAnimationPriority(int index) {
void Rig::restoreJointRotation(int index, float fraction, float priority) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = 0.0f;
_jointStates[index].restoreRotation(fraction, priority);
}
}
AnimationHandlePointer Rig::createAnimationHandle() {
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
_animationHandles.insert(handle);
return handle;
}
bool Rig::getJointStateAtIndex(int jointIndex, JointState& jointState) const {
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
jointState = _jointStates[jointIndex];
// position is in world-frame
position = translation + rotation * _jointStates[jointIndex].getPosition();
return true;
}
void Rig::updateJointStates(glm::mat4 parentTransform) {
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in model-frame
position = extractTranslation(_jointStates[jointIndex].getTransform());
return true;
}
bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in world-frame
position = translation + rotation * _jointStates[jointIndex].getVisiblePosition();
return true;
}
bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getVisibleRotation();
return true;
}
glm::mat4 Rig::getJointTransform(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return glm::mat4();
}
return _jointStates[jointIndex].getTransform();
}
glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return glm::mat4();
}
return _jointStates[jointIndex].getVisibleTransform();
}
void Rig::simulateInternal(glm::mat4 parentTransform) {
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i, parentTransform);
}
}
void Rig::updateJointState(int index, glm::mat4 parentTransform) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
// glm::mat4 parentTransform = glm::scale(scale) * glm::translate(offset) * geometryOffset;
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}
void Rig::resetAllTransformsChanged() {
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].resetTransformChanged();
}
}
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInBindFrame(rotation, priority, constrain);
endRotation = state.getRotationInBindFrame();
return endRotation;
}
glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInConstrainedFrame(targetRotation, priority, constrain);
endRotation = state.getRotationInConstrainedFrame();
return endRotation;
}
void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return;
}
_jointStates[jointIndex].applyRotationDelta(delta, constrain, priority);
}
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment,
float priority, glm::mat4 parentTransform) {
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
// const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint();
const QVector<int>& freeLineage = fbxJoint.freeLineage;
if (freeLineage.isEmpty()) {
return false;
}
@ -304,19 +328,14 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
return true;
}
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
const glm::quat& targetRotation, float priority, glm::mat4 parentTransform) {
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
// NOTE: targetRotation is from bind- to model-frame
if (endIndex == -1 || _jointStates.isEmpty()) {
return;
}
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
// const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
const FBXJoint& fbxJoint = _jointStates[endIndex].getFBXJoint();
const QVector<int>& freeLineage = fbxJoint.freeLineage;
if (freeLineage.isEmpty()) {
return;
}
@ -404,11 +423,11 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
}
// recompute transforms from the top down
glm::mat4 parentTransform = topParentTransform;
glm::mat4 currentParentTransform = topParentTransform;
for (int j = numFree - 1; j >= 0; --j) {
JointState& freeState = _jointStates[freeLineage.at(j)];
freeState.computeTransform(parentTransform);
parentTransform = freeState.getTransform();
freeState.computeTransform(currentParentTransform);
currentParentTransform = freeState.getTransform();
}
// measure our success
@ -420,14 +439,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
endState.setRotationInBindFrame(targetRotation, priority, true);
}
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) {
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
// const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint();
const QVector<int>& freeLineage = fbxJoint.freeLineage;
foreach (int index, freeLineage) {
JointState& state = _jointStates[index];
@ -436,22 +451,78 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) {
return true;
}
float Rig::getLimbLength(int jointIndex, glm::vec3 scale) const {
float Rig::getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return 0.0f;
}
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
// const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
const FBXJoint& fbxJoint = _jointStates[jointIndex].getFBXJoint();
const QVector<int>& freeLineage = fbxJoint.freeLineage;
float length = 0.0f;
float lengthScale = (scale.x + scale.y + scale.z) / 3.0f;
for (int i = freeLineage.size() - 2; i >= 0; i--) {
int something = freeLineage.at(i);
const FBXJoint& fbxJointI = _jointStates[something].getFBXJoint();
length += fbxJointI.distanceToParent * lengthScale;
length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale;
}
return length;
}
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInBindFrame(rotation, priority, constrain);
endRotation = state.getRotationInBindFrame();
return endRotation;
}
glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return glm::vec3();
}
return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame();
}
glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInConstrainedFrame(targetRotation, priority, constrain);
endRotation = state.getRotationInConstrainedFrame();
return endRotation;
}
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;
}
_jointStates[jointIndex].setVisibleTransform(newTransform);
}
void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return;
}
_jointStates[jointIndex].applyRotationDelta(delta, constrain, priority);
}
glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return glm::quat();
}
return _jointStates[jointIndex].getDefaultRotationInParentFrame();
}

View file

@ -11,14 +11,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* TBD:
- What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop?
Is there common/copied code (e.g., ScriptableAvatar::update)?
- What iare responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop?
Is there common/copied code (e.g., ScriptableAvatar::update)?
- How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object physics?
- Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly?
- Distribute some doc from here to the right files if it turns out to be correct:
- AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything equivalent to editEntity.
But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject is to AnimationPointer?
But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject is to AnimationPointer?
*/
#ifndef __hifi__Rig__
@ -39,49 +39,69 @@ typedef std::shared_ptr<Rig> RigPointer;
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
public:
virtual ~Rig() {}
RigPointer getRigPointer() { return shared_from_this(); }
AnimationHandlePointer createAnimationHandle();
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
void addRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningAnimation(AnimationHandlePointer animationHandle);
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
float initJointStates(glm::vec3 scale, glm::vec3 offset, QVector<JointState> states);
void initJointTransforms(glm::vec3 scale, glm::vec3 offset);
void resetJoints();
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform);
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
int jointStateCount() const { return _jointStates.size(); }
bool getJointStateAtIndex(int jointIndex, JointState& jointState) const;
int getJointStateCount() const { return _jointStates.size(); }
void updateJointStates(glm::mat4 parentTransform);
void updateJointState(int index, glm::mat4 parentTransform);
void resetAllTransformsChanged();
bool getJointState(int index, glm::quat& rotation) const;
void initJointTransforms(glm::mat4 parentTransform);
void clearJointTransformTranslation(int jointIndex);
void reset(const QVector<FBXJoint>& fbxJoints);
bool getJointStateRotation(int index, glm::quat& rotation) const;
void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority);
JointState getJointState(int jointIndex) const;
bool getVisibleJointState(int index, glm::quat& rotation) const;
void updateVisibleJointStates();
void clearJointState(int index);
void clearJointStates();
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
void clearJointAnimationPriority(int index);
void setJointAnimatinoPriority(int index, float newPriority);
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
void restoreJointRotation(int index, float fraction, float priority);
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const;
bool getJointPosition(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
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);
void simulateInternal(glm::mat4 parentTransform);
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform);
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform);
bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage);
float getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const;
glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false);
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
float priority, bool constrain = false);
void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority);
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
void updateVisibleJointStates();
QVector<JointState> getJointStates() { return _jointStates; }
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
virtual void updateFaceJointState(int index, glm::mat4 parentTransform) = 0;
AnimationHandlePointer createAnimationHandle();
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment,
float priority, glm::mat4 parentTransform);
void inverseKinematics(int endIndex, glm::vec3 targetPosition,
const glm::quat& targetRotation, float priority, glm::mat4 parentTransform);
bool restoreJointPosition(int jointIndex, float fraction, float priority);
float getLimbLength(int jointIndex, glm::vec3 scale) const;
protected:
protected:
QVector<JointState> _jointStates;
QSet<AnimationHandlePointer> _animationHandles;

View file

@ -40,6 +40,7 @@
#include "RenderablePolyVoxEntityItem.h"
#include "EntitiesRendererLogging.h"
#include "AddressManager.h"
#include "EntityRig.h"
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :
@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll
return model;
}
model = new Model();
model = new Model(std::make_shared<EntityRig>());
model->init();
model->setURL(QUrl(url));
model->setCollisionModelURL(QUrl(collisionUrl));
@ -728,7 +729,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c
}
// create the model and correctly initialize it with the new url
model = new Model();
model = new Model(std::make_shared<EntityRig>());
model->init();
model->setURL(QUrl(newUrl));
model->setCollisionModelURL(QUrl(collisionUrl));

View file

@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
}
Model* RenderableZoneEntityItem::getModel() {
Model* model = new Model();
Model* model = new Model(nullptr);
model->setIsWireframe(true);
model->init();
return model;

View file

@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<Net
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
Model::Model(QObject* parent, RigPointer rig) :
Model::Model(RigPointer rig, QObject* parent) :
QObject(parent),
_scale(1.0f, 1.0f, 1.0f),
_scaleToFit(false),
@ -87,7 +87,7 @@ Model::Model(QObject* parent, RigPointer rig) :
if (_viewState) {
moveToThread(_viewState->getMainThread());
}
setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));
}
@ -252,22 +252,9 @@ QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) {
};
void Model::initJointTransforms() {
// compute model transforms
int numStates = _jointStates.size();
for (int i = 0; i < numStates; ++i) {
JointState& state = _jointStates[i];
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
// NOTE: in practice geometry.offset has a non-unity scale (rather than a translation)
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
state.initTransform(parentTransform);
} else {
const JointState& parentState = _jointStates.at(parentIndex);
state.initTransform(parentState.getTransform());
}
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->initJointTransforms(parentTransform);
}
void Model::init() {
@ -396,14 +383,8 @@ void Model::init() {
}
void Model::reset() {
if (_jointStates.isEmpty()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
}
_rig->reset(geometry.joints);
_meshGroupsKnown = false;
_readyWhenAdded = false; // in case any of our users are using scenes
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
@ -436,28 +417,30 @@ bool Model::updateGeometry() {
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
QVector<JointState> newJointStates = createJointStates(newGeometry);
if (! _jointStates.isEmpty()) {
if (! _rig->jointStatesEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
it != oldGeometry.jointIndices.constEnd(); it++) {
int oldIndex = it.value() - 1;
int newIndex = newGeometry.getJointIndex(it.key());
if (newIndex != -1) {
newJointStates[newIndex].copyState(_jointStates[oldIndex]);
newJointStates[newIndex].copyState(_rig->getJointState(oldIndex));
}
}
}
}
deleteGeometry();
_dilatedTextures.clear();
setGeometry(geometry);
_meshGroupsKnown = false;
_readyWhenAdded = false; // in case any of our users are using scenes
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
initJointStates(newJointStates);
needToRebuild = true;
} else if (_jointStates.isEmpty()) {
} else if (_rig->jointStatesEmpty()) {
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
if (fbxGeometry.joints.size() > 0) {
initJointStates(createJointStates(fbxGeometry));
@ -493,22 +476,9 @@ bool Model::updateGeometry() {
// virtual
void Model::initJointStates(QVector<JointState> states) {
_jointStates = states;
initJointTransforms();
int numStates = _jointStates.size();
float radius = 0.0f;
for (int i = 0; i < numStates; ++i) {
float distance = glm::length(_jointStates[i].getPosition());
if (distance > radius) {
radius = distance;
}
_jointStates[i].buildConstraint();
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
_boundingRadius = radius;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_boundingRadius = _rig->initJointStates(states, parentTransform);
}
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
@ -1070,51 +1040,24 @@ glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
return translatedPoint;
}
bool Model::getJointState(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
const JointState& state = _jointStates.at(index);
rotation = state.getRotationInConstrainedFrame();
return !state.rotationIsDefault(rotation);
return _rig->getJointStateRotation(index, rotation);
}
bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
const JointState& state = _jointStates.at(index);
rotation = state.getVisibleRotationInConstrainedFrame();
return !state.rotationIsDefault(rotation);
return _rig->getVisibleJointState(index, rotation);
}
void Model::clearJointState(int index) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
}
_rig->clearJointState(index);
}
void Model::clearJointAnimationPriority(int index) {
if (_rig) {
_rig->clearJointAnimationPriority(index);
} else {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = 0.0f;
}
}
_rig->clearJointAnimationPriority(index);
}
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
if (valid) {
state.setRotationInConstrainedFrame(rotation, priority);
} else {
state.restoreRotation(1.0f, priority);
}
}
_rig->setJointState(index, valid, rotation, priority);
}
int Model::getParentJointIndex(int jointIndex) const {
@ -1189,62 +1132,31 @@ void Model::setCollisionModelURL(const QUrl& url) {
}
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in world-frame
position = _translation + _rotation * _jointStates[jointIndex].getPosition();
return true;
return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
}
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in model-frame
position = extractTranslation(_jointStates[jointIndex].getTransform());
return true;
return _rig->getJointPosition(jointIndex, position);
}
bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _rotation * _jointStates[jointIndex].getRotation();
return true;
return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation);
}
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _jointStates[jointIndex].getRotation();
return true;
return _rig->getJointRotation(jointIndex, rotation);
}
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _rotation * _jointStates[jointIndex].getRotation();
return true;
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
}
bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in world-frame
position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition();
return true;
return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
}
bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _rotation * _jointStates[jointIndex].getVisibleRotation();
return true;
return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation);
}
QStringList Model::getJointNames() const {
@ -1438,12 +1350,14 @@ void Model::updateClusterMatrices() {
if (_showTrueJointTransforms) {
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
state.clusterMatrices[j] =
modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
}
} else {
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;
state.clusterMatrices[j] =
modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
}
}
}
@ -1457,16 +1371,12 @@ void Model::simulateInternal(float deltaTime) {
handle->simulate(deltaTime);
}
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].resetTransformChanged();
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->simulateInternal(parentTransform);
_shapesAreDirty = !_shapes.isEmpty();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
@ -1474,12 +1384,14 @@ void Model::simulateInternal(float deltaTime) {
if (_showTrueJointTransforms) {
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
state.clusterMatrices[j] =
modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
}
} else {
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;
state.clusterMatrices[j] =
modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
}
}
}
@ -1492,261 +1404,42 @@ void Model::simulateInternal(float deltaTime) {
}
void Model::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}
void Model::updateVisibleJointStates() {
if (_showTrueJointTransforms) {
// no need to update visible transforms
return;
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->updateJointState(index, parentTransform);
}
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
if (freeLineage.isEmpty()) {
return false;
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
if (_rig->setJointPosition(jointIndex, position, rotation, useRotation,
lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) {
_shapesAreDirty = !_shapes.isEmpty();
return true;
}
if (lastFreeIndex == -1) {
lastFreeIndex = freeLineage.last();
}
// this is a cyclic coordinate descent algorithm: see
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
const int ITERATION_COUNT = 1;
glm::vec3 worldAlignment = alignment;
for (int i = 0; i < ITERATION_COUNT; i++) {
// first, try to rotate the end effector as close as possible to the target rotation, if any
glm::quat endRotation;
if (useRotation) {
JointState& state = _jointStates[jointIndex];
state.setRotationInBindFrame(rotation, priority);
endRotation = state.getRotationInBindFrame();
}
// then, we go from the joint upwards, rotating the end as close as possible to the target
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform());
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
int index = freeLineage.at(j);
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (!(joint.isFree || allIntermediatesFree)) {
continue;
}
glm::vec3 jointPosition = extractTranslation(state.getTransform());
glm::vec3 jointVector = endPosition - jointPosition;
glm::quat oldCombinedRotation = state.getRotation();
glm::quat combinedDelta;
float combinedWeight;
if (useRotation) {
combinedDelta = safeMix(rotation * glm::inverse(endRotation),
rotationBetween(jointVector, position - jointPosition), 0.5f);
combinedWeight = 2.0f;
} else {
combinedDelta = rotationBetween(jointVector, position - jointPosition);
combinedWeight = 1.0f;
}
if (alignment != glm::vec3() && j > 1) {
jointVector = endPosition - jointPosition;
glm::vec3 positionSum;
for (int k = j - 1; k > 0; k--) {
int index = freeLineage.at(k);
updateJointState(index);
positionSum += extractTranslation(_jointStates.at(index).getTransform());
}
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector));
glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
const float LENGTH_EPSILON = 0.001f;
if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
1.0f / (combinedWeight + 1.0f));
}
}
state.applyRotationDelta(combinedDelta, true, priority);
glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) {
endRotation = actualDelta * endRotation;
}
}
}
// now update the joint states from the top
for (int j = freeLineage.size() - 1; j >= 0; j--) {
updateJointState(freeLineage.at(j));
}
_shapesAreDirty = !_shapes.isEmpty();
return true;
return false;
}
void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {
// NOTE: targetRotation is from bind- to model-frame
if (endIndex == -1 || _jointStates.isEmpty()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
if (freeLineage.isEmpty()) {
return;
}
int numFree = freeLineage.size();
// store and remember topmost parent transform
glm::mat4 topParentTransform;
{
int index = freeLineage.last();
const JointState& state = _jointStates.at(index);
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
} else {
topParentTransform = _jointStates[parentIndex].getTransform();
}
}
// this is a cyclic coordinate descent algorithm: see
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
// keep track of the position of the end-effector
JointState& endState = _jointStates[endIndex];
glm::vec3 endPosition = endState.getPosition();
float distanceToGo = glm::distance(targetPosition, endPosition);
const int MAX_ITERATION_COUNT = 2;
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
int numIterations = 0;
do {
++numIterations;
// moving up, rotate each free joint to get endPosition closer to target
for (int j = 1; j < numFree; j++) {
int nextIndex = freeLineage.at(j);
JointState& nextState = _jointStates[nextIndex];
FBXJoint nextJoint = nextState.getFBXJoint();
if (! nextJoint.isFree) {
continue;
}
glm::vec3 pivot = nextState.getPosition();
glm::vec3 leverArm = endPosition - pivot;
float leverLength = glm::length(leverArm);
if (leverLength < EPSILON) {
continue;
}
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
// We want to mix the shortest rotation with one that will pull the system down with gravity
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
// where each joint has unit mass and we don't bother averaging it because we only need direction.
if (j > 1) {
glm::vec3 centerOfMass(0.0f);
for (int k = 0; k < j; ++k) {
int massIndex = freeLineage.at(k);
centerOfMass += _jointStates[massIndex].getPosition() - pivot;
}
// the gravitational effect is a rotation that tends to align the two cross products
const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f);
glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
glm::cross(worldAlignment, leverArm));
float gravityAngle = glm::angle(gravityDelta);
const float MIN_GRAVITY_ANGLE = 0.1f;
float mixFactor = 0.5f;
if (gravityAngle < MIN_GRAVITY_ANGLE) {
// the final rotation is a mix of the two
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
}
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
}
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
// in the process. This provides stability to the IK solution for most models.
glm::quat oldNextRotation = nextState.getRotation();
float mixFactor = 0.03f;
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
// measure the result of the rotation which may have been modified by
// blending and constraints
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
endPosition = pivot + actualDelta * leverArm;
}
// recompute transforms from the top down
glm::mat4 parentTransform = topParentTransform;
for (int j = numFree - 1; j >= 0; --j) {
JointState& freeState = _jointStates[freeLineage.at(j)];
freeState.computeTransform(parentTransform);
parentTransform = freeState.getTransform();
}
// measure our success
endPosition = endState.getPosition();
distanceToGo = glm::distance(targetPosition, endPosition);
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
// set final rotation of the end joint
endState.setRotationInBindFrame(targetRotation, priority, true);
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
_rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);
_shapesAreDirty = !_shapes.isEmpty();
}
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
foreach (int index, freeLineage) {
JointState& state = _jointStates[index];
state.restoreRotation(fraction, priority);
}
return true;
return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage);
}
float Model::getLimbLength(int jointIndex) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return 0.0f;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
float length = 0.0f;
float lengthScale = (_scale.x + _scale.y + _scale.z) / 3.0f;
for (int i = freeLineage.size() - 2; i >= 0; i--) {
length += geometry.joints.at(freeLineage.at(i)).distanceToParent * lengthScale;
}
return length;
return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);
}
void Model::renderJointCollisionShapes(float alpha) {
@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() {
void Model::deleteGeometry() {
_blendedVertexBuffers.clear();
_jointStates.clear();
_rig->clearJointStates();
_meshStates.clear();
clearShapes();
@ -1945,7 +1638,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
args, locations);
{
updateVisibleJointStates();
if (!_showTrueJointTransforms) {
_rig->updateVisibleJointStates();
} // else no need to update visible transforms
}
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown

View file

@ -64,7 +64,7 @@ public:
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
Model(QObject* parent = nullptr, RigPointer rig = nullptr);
Model(RigPointer rig, QObject* parent = nullptr);
virtual ~Model();
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
@ -162,7 +162,7 @@ public:
const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
/// Returns the number of joint states in the model.
int getJointStateCount() const { return _jointStates.size(); }
int getJointStateCount() const { return _rig->getJointStateCount(); }
/// Fetches the joint state at the specified index.
/// \return whether or not the joint state is "valid" (that is, non-default)
@ -224,9 +224,9 @@ public:
void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
QVector<JointState>& getJointStates() { return _jointStates; }
const QVector<JointState>& getJointStates() const { return _jointStates; }
// QVector<JointState>& getJointStates() { return _rig->getJointStates(); }
// const QVector<JointState>& getJointStates() const { return _jointStates; }
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url)
@ -259,8 +259,6 @@ protected:
glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to
bool _showTrueJointTransforms;
QVector<JointState> _jointStates;
class MeshState {
public:
@ -283,8 +281,6 @@ protected:
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
virtual void updateVisibleJointStates();
/// \param jointIndex index of joint in model structure
/// \param position position of joint in model-frame
/// \param rotation rotation of joint in model-frame