mirror of
https://github.com/overte-org/overte.git
synced 2025-04-26 08:56:26 +02:00
JointStates are owned by Rig objects. Model, FaceModel, SkeletonModel call into their Rig pointer to access JointStates.
This commit is contained in:
parent
47965bc39c
commit
ee334ff826
19 changed files with 562 additions and 635 deletions
|
@ -45,6 +45,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
|
#include "EntityRig.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -965,7 +966,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
if (_unusedAttachments.size() > 0) {
|
if (_unusedAttachments.size() > 0) {
|
||||||
model = _unusedAttachments.takeFirst();
|
model = _unusedAttachments.takeFirst();
|
||||||
} else {
|
} else {
|
||||||
model = new Model(this);
|
model = new Model(std::make_shared<EntityRig>(), this);
|
||||||
}
|
}
|
||||||
model->init();
|
model->init();
|
||||||
_attachmentModels.append(model);
|
_attachmentModels.append(model);
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
#include "SceneScriptingInterface.h"
|
#include "SceneScriptingInterface.h"
|
||||||
|
#include "AvatarRig.h"
|
||||||
|
|
||||||
// 70 times per second - target is 60hz, but this helps account for any small deviations
|
// 70 times per second - target is 60hz, but this helps account for any small deviations
|
||||||
// in the update loop
|
// 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
|
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||||
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
|
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();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
||||||
|
@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||||
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<Rig>()));
|
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<AvatarRig>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
|
||||||
FaceModel::FaceModel(Head* owningHead) :
|
FaceModel::FaceModel(Head* owningHead, RigPointer rig) :
|
||||||
|
Model(rig, nullptr),
|
||||||
_owningHead(owningHead)
|
_owningHead(owningHead)
|
||||||
{
|
{
|
||||||
|
assert(_rig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
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
|
// get the rotation axes in joint space and use them to adjust the rotation
|
||||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
|
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)));
|
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||||
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
|
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
|
||||||
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
|
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
|
||||||
_owningHead->getTorsoTwist(),
|
_owningHead->getTorsoTwist(),
|
||||||
_owningHead->getFinalLeanSideways()));
|
_owningHead->getFinalLeanSideways()));
|
||||||
pitchYawRoll -= lean;
|
pitchYawRoll -= lean;
|
||||||
state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
|
_rig->setJointRotationInConstrainedFrame(index,
|
||||||
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
|
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
|
||||||
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
|
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
|
||||||
* joint.rotation, DEFAULT_PRIORITY);
|
* 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
|
// 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.
|
// 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::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
|
||||||
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
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 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
|
||||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() +
|
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() +
|
||||||
_owningHead->getSaccade() - model->getTranslation(), 1.0f));
|
_owningHead->getSaccade() - model->getTranslation(), 1.0f));
|
||||||
glm::quat between = rotationBetween(front, lookAt);
|
glm::quat between = rotationBetween(front, lookAt);
|
||||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
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)) *
|
_rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
|
||||||
joint.rotation, DEFAULT_PRIORITY);
|
-MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||||
|
joint.rotation, DEFAULT_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FaceModel::updateJointState(int index) {
|
void FaceModel::updateJointState(int index) {
|
||||||
JointState& state = _jointStates[index];
|
const JointState& state = _rig->getJointState(index);
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
|
||||||
// guard against out-of-bounds access to _jointStates
|
// guard against out-of-bounds access to _jointStates
|
||||||
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) {
|
||||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
const JointState& parentState = _rig->getJointState(joint.parentIndex);
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
if (index == geometry.neckJointIndex) {
|
if (index == geometry.neckJointIndex) {
|
||||||
maybeUpdateNeckRotation(parentState, joint, state);
|
maybeUpdateNeckRotation(parentState, joint, index);
|
||||||
|
|
||||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
} 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 {
|
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||||
|
|
|
@ -22,12 +22,12 @@ class FaceModel : public Model {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FaceModel(Head* owningHead);
|
FaceModel(Head* owningHead, RigPointer rig);
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||||
|
|
||||||
virtual void maybeUpdateNeckRotation(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, JointState& state);
|
virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index);
|
||||||
virtual void updateJointState(int index);
|
virtual void updateJointState(int index);
|
||||||
|
|
||||||
/// Retrieve the positions of up to two eye meshes.
|
/// Retrieve the positions of up to two eye meshes.
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "devices/DdeFaceTracker.h"
|
#include "devices/DdeFaceTracker.h"
|
||||||
#include "devices/Faceshift.h"
|
#include "devices/Faceshift.h"
|
||||||
|
#include "AvatarRig.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -55,11 +56,10 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_deltaLeanForward(0.0f),
|
_deltaLeanForward(0.0f),
|
||||||
_isCameraMoving(false),
|
_isCameraMoving(false),
|
||||||
_isLookingAtMe(false),
|
_isLookingAtMe(false),
|
||||||
_faceModel(this),
|
_faceModel(this, std::make_shared<AvatarRig>()),
|
||||||
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
|
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||||
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
|
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::init() {
|
void Head::init() {
|
||||||
|
|
|
@ -30,7 +30,7 @@ enum StandingFootState {
|
||||||
};
|
};
|
||||||
|
|
||||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
||||||
Model(parent, rig),
|
Model(rig, parent),
|
||||||
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
|
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||||
_owningAvatar(owningAvatar),
|
_owningAvatar(owningAvatar),
|
||||||
_boundingShape(),
|
_boundingShape(),
|
||||||
|
@ -51,11 +51,13 @@ SkeletonModel::~SkeletonModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
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
|
// Determine the default eye position for avatar scale = 1.0
|
||||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||||
if (0 <= headJointIndex && headJointIndex < _jointStates.size()) {
|
if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) {
|
||||||
|
|
||||||
glm::vec3 leftEyePosition, rightEyePosition;
|
glm::vec3 leftEyePosition, rightEyePosition;
|
||||||
getEyeModelPositions(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
|
// 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
|
// of its root joint and we need that done before we try to build shapes hence we
|
||||||
// recompute all joint transforms at this time.
|
// recompute all joint transforms at this time.
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||||
updateJointState(i);
|
_rig->updateJointState(i, parentTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearShapes();
|
clearShapes();
|
||||||
|
@ -168,7 +170,7 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes)
|
||||||
|| jointIndex == getRightHandJointIndex()) {
|
|| jointIndex == getRightHandJointIndex()) {
|
||||||
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
|
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
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];
|
const FBXJoint& joint = geometry.joints[i];
|
||||||
int parentIndex = joint.parentIndex;
|
int parentIndex = joint.parentIndex;
|
||||||
Shape* shape = _shapes[i];
|
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) {
|
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// NOTE: 'position' is in model-frame
|
// NOTE: 'position' is in model-frame
|
||||||
|
@ -226,16 +228,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
||||||
if (forearmLength < EPSILON) {
|
if (forearmLength < EPSILON) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JointState& state = _jointStates[jointIndex];
|
glm::quat handRotation;
|
||||||
glm::quat handRotation = state.getRotation();
|
if (!_rig->getJointStateRotation(jointIndex, handRotation)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// align hand with forearm
|
// align hand with forearm
|
||||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
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) {
|
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
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);
|
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||||
setJointPosition(parentJointIndex, palmPosition + forearm,
|
setJointPosition(parentJointIndex, palmPosition + forearm,
|
||||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||||
JointState& parentState = _jointStates[parentJointIndex];
|
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
|
||||||
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
|
|
||||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
// 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 {
|
} else {
|
||||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::updateJointState(int index) {
|
void SkeletonModel::updateJointState(int index) {
|
||||||
if (index < 0 && index >= _jointStates.size()) {
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
return; // bail
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
}
|
|
||||||
JointState& state = _jointStates[index];
|
const JointState joint = _rig->getJointState(index);
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
if (joint.getParentIndex() >= 0 && joint.getParentIndex() < _rig->getJointStateCount()) {
|
||||||
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
const JointState parentState = _rig->getJointState(joint.getParentIndex());
|
||||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
if (index == geometry.leanJointIndex) {
|
if (index == geometry.leanJointIndex) {
|
||||||
maybeUpdateLeanRotation(parentState, state);
|
maybeUpdateLeanRotation(parentState, index);
|
||||||
|
|
||||||
} else if (index == geometry.neckJointIndex) {
|
} else if (index == geometry.neckJointIndex) {
|
||||||
maybeUpdateNeckRotation(parentState, joint, state);
|
maybeUpdateNeckRotation(parentState, joint.getFBXJoint(), index);
|
||||||
|
|
||||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
} 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) {
|
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()) {
|
if (!_owningAvatar->isMyAvatar()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -305,24 +308,24 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, Joint
|
||||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||||
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
glm::quat inverse = glm::inverse(parentState.getRotation() * _rig->getJointDefaultRotationInParentFrame(index));
|
||||||
state.setRotationInConstrainedFrame(
|
_rig->setJointRotationInConstrainedFrame(index,
|
||||||
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
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()->getFinalLeanForward(), inverse * xAxis)
|
||||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
|
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
|
||||||
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
* _rig->getJointState(index).getFBXJoint().rotation, LEAN_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
|
||||||
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state);
|
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index) {
|
||||||
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state);
|
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
@ -331,9 +334,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
||||||
batch._glLineWidth(3.0f);
|
batch._glLineWidth(3.0f);
|
||||||
do {
|
do {
|
||||||
const FBXJoint& joint = geometry.joints.at(jointIndex);
|
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::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;
|
float fanScale = directionSize * 0.75f;
|
||||||
|
|
||||||
Transform transform = Transform();
|
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::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
||||||
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
||||||
|
|
||||||
JointState& shoulderState = _jointStates[shoulderJointIndex];
|
_rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
|
||||||
shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY);
|
_rig->setJointRotationInBindFrame(elbowJointIndex,
|
||||||
|
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
|
||||||
JointState& elbowState = _jointStates[elbowJointIndex];
|
shoulderRotation, PALM_PRIORITY);
|
||||||
elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
|
_rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY);
|
||||||
|
|
||||||
JointState& handState = _jointStates[jointIndex];
|
|
||||||
handState.setRotationInBindFrame(rotation, PALM_PRIORITY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
||||||
|
@ -526,7 +528,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP
|
||||||
glm::quat worldFrameRotation;
|
glm::quat worldFrameRotation;
|
||||||
bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
|
bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
|
||||||
if (success) {
|
if (success) {
|
||||||
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
|
neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation;
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -636,7 +638,7 @@ float VERY_BIG_MASS = 1.0e6f;
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void SkeletonModel::buildShapes() {
|
void SkeletonModel::buildShapes() {
|
||||||
if (_geometry == NULL || _jointStates.isEmpty()) {
|
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,9 +649,8 @@ void SkeletonModel::buildShapes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float uniformScale = extractUniformScale(_scale);
|
float uniformScale = extractUniformScale(_scale);
|
||||||
const int numStates = _jointStates.size();
|
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||||
for (int i = 0; i < numStates; i++) {
|
const JointState& state = _rig->getJointState(i);
|
||||||
JointState& state = _jointStates[i];
|
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
float radius = uniformScale * joint.boneRadius;
|
float radius = uniformScale * joint.boneRadius;
|
||||||
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
|
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
|
||||||
|
@ -683,7 +684,7 @@ void SkeletonModel::buildShapes() {
|
||||||
|
|
||||||
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
// compute default joint transforms
|
// compute default joint transforms
|
||||||
int numStates = _jointStates.size();
|
int numStates = _rig->getJointStateCount();
|
||||||
assert(numStates == _shapes.size());
|
assert(numStates == _shapes.size());
|
||||||
QVector<glm::mat4> transforms;
|
QVector<glm::mat4> transforms;
|
||||||
transforms.fill(glm::mat4(), numStates);
|
transforms.fill(glm::mat4(), numStates);
|
||||||
|
@ -694,11 +695,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
totalExtents.addPoint(glm::vec3(0.0f));
|
totalExtents.addPoint(glm::vec3(0.0f));
|
||||||
for (int i = 0; i < numStates; i++) {
|
for (int i = 0; i < numStates; i++) {
|
||||||
// compute the default transform of this joint
|
// compute the default transform of this joint
|
||||||
JointState& state = _jointStates[i];
|
const JointState& state = _rig->getJointState(i);
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
int parentIndex = joint.parentIndex;
|
int parentIndex = joint.parentIndex;
|
||||||
if (parentIndex == -1) {
|
if (parentIndex == -1) {
|
||||||
transforms[i] = _jointStates[i].getTransform();
|
transforms[i] = _rig->getJointTransform(i);
|
||||||
} else {
|
} else {
|
||||||
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||||
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
||||||
|
@ -748,7 +749,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
_boundingShape.setRadius(capsuleRadius);
|
_boundingShape.setRadius(capsuleRadius);
|
||||||
_boundingShape.setHalfHeight(0.5f * diagonal.y - 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;
|
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||||
_boundingRadius = 0.5f * glm::length(diagonal);
|
_boundingRadius = 0.5f * glm::length(diagonal);
|
||||||
}
|
}
|
||||||
|
@ -853,7 +854,7 @@ void SkeletonModel::cauterizeHead() {
|
||||||
if (isActive()) {
|
if (isActive()) {
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const int neckJointIndex = geometry.neckJointIndex;
|
const int neckJointIndex = geometry.neckJointIndex;
|
||||||
if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) {
|
if (neckJointIndex > 0 && neckJointIndex < _rig->getJointStateCount()) {
|
||||||
|
|
||||||
// lazy init of headBones
|
// lazy init of headBones
|
||||||
if (_headBones.size() == 0) {
|
if (_headBones.size() == 0) {
|
||||||
|
@ -861,13 +862,13 @@ void SkeletonModel::cauterizeHead() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// preserve the translation for the neck
|
// 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);
|
glm::vec4 zero(0, 0, 0, 0);
|
||||||
for (const int &i : _headBones) {
|
for (const int &i : _headBones) {
|
||||||
JointState& joint = _jointStates[i];
|
|
||||||
glm::mat4 newXform(zero, zero, zero, trans);
|
glm::mat4 newXform(zero, zero, zero, trans);
|
||||||
joint.setTransform(newXform);
|
_rig->setJointTransform(i, newXform);
|
||||||
joint.setVisibleTransform(newXform);
|
_rig->setJointVisibleTransform(i, newXform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,9 +134,9 @@ protected:
|
||||||
/// Updates the state of the joint at the specified index.
|
/// Updates the state of the joint at the specified index.
|
||||||
virtual void updateJointState(int index);
|
virtual void updateJointState(int index);
|
||||||
|
|
||||||
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
|
void maybeUpdateLeanRotation(const JointState& parentState, int index);
|
||||||
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
|
||||||
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, int index);
|
||||||
|
|
||||||
void cauterizeHead();
|
void cauterizeHead();
|
||||||
void initHeadBones();
|
void initHeadBones();
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay()
|
ModelOverlay::ModelOverlay()
|
||||||
: _model(),
|
: _model(nullptr),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_updateModel(false)
|
_updateModel(false)
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ ModelOverlay::ModelOverlay()
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||||
Volume3DOverlay(modelOverlay),
|
Volume3DOverlay(modelOverlay),
|
||||||
_model(),
|
_model(nullptr),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_url(modelOverlay->_url),
|
_url(modelOverlay->_url),
|
||||||
_updateModel(false)
|
_updateModel(false)
|
||||||
|
|
|
@ -159,15 +159,15 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
||||||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
||||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
||||||
float frameFraction = glm::fract(frameIndex);
|
float frameFraction = glm::fract(frameIndex);
|
||||||
QVector<JointState> jointStates = _rig->getJointStates();
|
assert(_rig->getJointStateCount() >= _jointMappings.size());
|
||||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||||
int mapping = _jointMappings.at(i);
|
int mapping = _jointMappings.at(i);
|
||||||
if (mapping != -1) {
|
if (mapping != -1) {
|
||||||
JointState& state = jointStates[mapping];
|
_rig->setJointRotationInConstrainedFrame(mapping,
|
||||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i),
|
safeMix(floorFrame.rotations.at(i),
|
||||||
ceilFrame.rotations.at(i),
|
ceilFrame.rotations.at(i),
|
||||||
frameFraction),
|
frameFraction),
|
||||||
_priority);
|
_priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,10 +176,9 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
|
||||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||||
int mapping = _jointMappings.at(i);
|
int mapping = _jointMappings.at(i);
|
||||||
if (mapping != -1) {
|
if (mapping != -1) {
|
||||||
QVector<JointState> jointStates = _rig->getJointStates();
|
JointState state = _rig->getJointState(mapping);
|
||||||
JointState& state = jointStates[mapping];
|
|
||||||
if (_priority == state._animationPriority) {
|
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++) {
|
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||||
int mapping = _jointMappings.at(i);
|
int mapping = _jointMappings.at(i);
|
||||||
if (mapping != -1) {
|
if (mapping != -1) {
|
||||||
QVector<JointState> jointStates = _rig->getJointStates();
|
JointState state = _rig->getJointState(mapping);
|
||||||
JointState& state = jointStates[mapping];
|
_rig->restoreJointRotation(mapping, 1.0f, state._animationPriority);
|
||||||
state.restoreRotation(1.0f, state._animationPriority);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
libraries/animation/src/AvatarRig.cpp
Normal file
51
libraries/animation/src/AvatarRig.cpp
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
libraries/animation/src/AvatarRig.h
Normal file
28
libraries/animation/src/AvatarRig.h
Normal 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
|
30
libraries/animation/src/EntityRig.cpp
Normal file
30
libraries/animation/src/EntityRig.cpp
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
libraries/animation/src/EntityRig.h
Normal file
28
libraries/animation/src/EntityRig.h
Normal 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
|
|
@ -13,11 +13,7 @@
|
||||||
|
|
||||||
#include "Rig.h"
|
#include "Rig.h"
|
||||||
|
|
||||||
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
|
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||||
return _runningAnimations.removeOne(animationHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
|
||||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||||
if (handle->getPriority() > (*it)->getPriority()) {
|
if (handle->getPriority() > (*it)->getPriority()) {
|
||||||
handles.insert(it, handle);
|
handles.insert(it, handle);
|
||||||
|
@ -27,17 +23,27 @@ static void insertSorted(QList<AnimationHandlePointer>& handles, const Animation
|
||||||
handles.append(handle);
|
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) {
|
void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||||
insertSorted(_runningAnimations, animationHandle);
|
insertSorted(_runningAnimations, animationHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||||
return _runningAnimations.contains(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;
|
_jointStates = states;
|
||||||
initJointTransforms(scale, offset);
|
initJointTransforms(parentTransform);
|
||||||
|
|
||||||
int numStates = _jointStates.size();
|
int numStates = _jointStates.size();
|
||||||
float radius = 0.0f;
|
float radius = 0.0f;
|
||||||
|
@ -52,11 +58,11 @@ float Rig::initJointStates(glm::vec3 scale, glm::vec3 offset, QVector<JointState
|
||||||
_jointStates[i].slaveVisibleTransform();
|
_jointStates[i].slaveVisibleTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX update AnimationHandles from here?
|
|
||||||
return radius;
|
return radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::initJointTransforms(glm::vec3 scale, glm::vec3 offset) {
|
|
||||||
|
void Rig::initJointTransforms(glm::mat4 parentTransform) {
|
||||||
// compute model transforms
|
// compute model transforms
|
||||||
int numStates = _jointStates.size();
|
int numStates = _jointStates.size();
|
||||||
for (int i = 0; i < numStates; ++i) {
|
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();
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
int parentIndex = joint.parentIndex;
|
int parentIndex = joint.parentIndex;
|
||||||
if (parentIndex == -1) {
|
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);
|
state.initTransform(parentTransform);
|
||||||
} else {
|
} else {
|
||||||
const JointState& parentState = _jointStates.at(parentIndex);
|
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()) {
|
if (_jointStates.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
const FBXJoint& fbxJoint = _jointStates[i].getFBXJoint();
|
_jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f);
|
||||||
_jointStates[i].setRotationInConstrainedFrame(fbxJoint.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()) {
|
if (index == -1 || index >= _jointStates.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -105,12 +119,6 @@ bool Rig::getVisibleJointState(int index, glm::quat& rotation) const {
|
||||||
return !state.rotationIsDefault(rotation);
|
return !state.rotationIsDefault(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateVisibleJointStates() {
|
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
|
||||||
_jointStates[i].slaveVisibleTransform();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rig::clearJointState(int index) {
|
void Rig::clearJointState(int index) {
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
if (index != -1 && index < _jointStates.size()) {
|
||||||
JointState& state = _jointStates[index];
|
JointState& state = _jointStates[index];
|
||||||
|
@ -122,6 +130,18 @@ void Rig::clearJointStates() {
|
||||||
_jointStates.clear();
|
_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) {
|
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
if (index != -1 && index < _jointStates.size()) {
|
||||||
JointState& state = _jointStates[index];
|
JointState& state = _jointStates[index];
|
||||||
|
@ -133,99 +153,103 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Rig::restoreJointRotation(int index, float fraction, float priority) {
|
||||||
void Rig::clearJointAnimationPriority(int index) {
|
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
if (index != -1 && index < _jointStates.size()) {
|
||||||
_jointStates[index]._animationPriority = 0.0f;
|
_jointStates[index].restoreRotation(fraction, priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationHandlePointer Rig::createAnimationHandle() {
|
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||||
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
|
glm::vec3 translation, glm::quat rotation) const {
|
||||||
_animationHandles.insert(handle);
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Rig::getJointStateAtIndex(int jointIndex, JointState& jointState) const {
|
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
jointState = _jointStates[jointIndex];
|
// position is in world-frame
|
||||||
|
position = translation + rotation * _jointStates[jointIndex].getPosition();
|
||||||
return true;
|
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++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
updateJointState(i, parentTransform);
|
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++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
_jointStates[i].resetTransformChanged();
|
_jointStates[i].resetTransformChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
|
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||||
glm::quat endRotation;
|
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||||
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) {
|
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
return false;
|
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()) {
|
if (freeLineage.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -304,19 +328,14 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
|
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||||
const glm::quat& targetRotation, float priority, glm::mat4 parentTransform) {
|
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||||
// NOTE: targetRotation is from bind- to model-frame
|
// NOTE: targetRotation is from bind- to model-frame
|
||||||
|
|
||||||
if (endIndex == -1 || _jointStates.isEmpty()) {
|
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||||
return;
|
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()) {
|
if (freeLineage.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -404,11 +423,11 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
// recompute transforms from the top down
|
// recompute transforms from the top down
|
||||||
glm::mat4 parentTransform = topParentTransform;
|
glm::mat4 currentParentTransform = topParentTransform;
|
||||||
for (int j = numFree - 1; j >= 0; --j) {
|
for (int j = numFree - 1; j >= 0; --j) {
|
||||||
JointState& freeState = _jointStates[freeLineage.at(j)];
|
JointState& freeState = _jointStates[freeLineage.at(j)];
|
||||||
freeState.computeTransform(parentTransform);
|
freeState.computeTransform(currentParentTransform);
|
||||||
parentTransform = freeState.getTransform();
|
currentParentTransform = freeState.getTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
// measure our success
|
// measure our success
|
||||||
|
@ -420,14 +439,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition,
|
||||||
endState.setRotationInBindFrame(targetRotation, priority, true);
|
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()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
return false;
|
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) {
|
foreach (int index, freeLineage) {
|
||||||
JointState& state = _jointStates[index];
|
JointState& state = _jointStates[index];
|
||||||
|
@ -436,22 +451,78 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||||
return true;
|
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()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
return 0.0f;
|
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 length = 0.0f;
|
||||||
float lengthScale = (scale.x + scale.y + scale.z) / 3.0f;
|
float lengthScale = (scale.x + scale.y + scale.z) / 3.0f;
|
||||||
for (int i = freeLineage.size() - 2; i >= 0; i--) {
|
for (int i = freeLineage.size() - 2; i >= 0; i--) {
|
||||||
int something = freeLineage.at(i);
|
length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale;
|
||||||
const FBXJoint& fbxJointI = _jointStates[something].getFBXJoint();
|
|
||||||
length += fbxJointI.distanceToParent * lengthScale;
|
|
||||||
}
|
}
|
||||||
return length;
|
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();
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
/* TBD:
|
/* TBD:
|
||||||
- What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? Is there common/copied code (e.g., ScriptableAvatar::update)?
|
- What is responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails? 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?
|
- 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?
|
- Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the system choosing randomly?
|
||||||
|
|
||||||
|
@ -37,49 +37,69 @@ typedef std::shared_ptr<Rig> RigPointer;
|
||||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
virtual ~Rig() {}
|
||||||
|
|
||||||
RigPointer getRigPointer() { return shared_from_this(); }
|
RigPointer getRigPointer() { return shared_from_this(); }
|
||||||
|
|
||||||
|
AnimationHandlePointer createAnimationHandle();
|
||||||
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
void addRunningAnimation(AnimationHandlePointer animationHandle);
|
void addRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
bool isRunningAnimation(AnimationHandlePointer animationHandle);
|
bool isRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
|
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
|
||||||
|
|
||||||
float initJointStates(glm::vec3 scale, glm::vec3 offset, QVector<JointState> states);
|
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform);
|
||||||
void initJointTransforms(glm::vec3 scale, glm::vec3 offset);
|
|
||||||
void resetJoints();
|
|
||||||
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
||||||
int jointStateCount() const { return _jointStates.size(); }
|
int getJointStateCount() const { return _jointStates.size(); }
|
||||||
bool getJointStateAtIndex(int jointIndex, JointState& jointState) const;
|
|
||||||
|
|
||||||
void updateJointStates(glm::mat4 parentTransform);
|
void initJointTransforms(glm::mat4 parentTransform);
|
||||||
void updateJointState(int index, glm::mat4 parentTransform);
|
void clearJointTransformTranslation(int jointIndex);
|
||||||
void resetAllTransformsChanged();
|
void reset(const QVector<FBXJoint>& fbxJoints);
|
||||||
|
bool getJointStateRotation(int index, glm::quat& rotation) const;
|
||||||
bool getJointState(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;
|
bool getVisibleJointState(int index, glm::quat& rotation) const;
|
||||||
void updateVisibleJointStates();
|
|
||||||
void clearJointState(int index);
|
void clearJointState(int index);
|
||||||
void clearJointStates();
|
void clearJointStates();
|
||||||
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
|
|
||||||
void clearJointAnimationPriority(int index);
|
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::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,
|
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
|
||||||
float priority, bool constrain = false);
|
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();
|
protected:
|
||||||
|
|
||||||
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:
|
|
||||||
QVector<JointState> _jointStates;
|
QVector<JointState> _jointStates;
|
||||||
|
|
||||||
QSet<AnimationHandlePointer> _animationHandles;
|
QSet<AnimationHandlePointer> _animationHandles;
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "RenderablePolyVoxEntityItem.h"
|
#include "RenderablePolyVoxEntityItem.h"
|
||||||
#include "EntitiesRendererLogging.h"
|
#include "EntitiesRendererLogging.h"
|
||||||
#include "AddressManager.h"
|
#include "AddressManager.h"
|
||||||
|
#include "EntityRig.h"
|
||||||
|
|
||||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||||
AbstractScriptingServicesInterface* scriptingServices) :
|
AbstractScriptingServicesInterface* scriptingServices) :
|
||||||
|
@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
model = new Model();
|
model = new Model(std::make_shared<EntityRig>());
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(url));
|
model->setURL(QUrl(url));
|
||||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
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
|
// create the model and correctly initialize it with the new url
|
||||||
model = new Model();
|
model = new Model(std::make_shared<EntityRig>());
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(newUrl));
|
model->setURL(QUrl(newUrl));
|
||||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
model->setCollisionModelURL(QUrl(collisionUrl));
|
||||||
|
|
|
@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
||||||
}
|
}
|
||||||
|
|
||||||
Model* RenderableZoneEntityItem::getModel() {
|
Model* RenderableZoneEntityItem::getModel() {
|
||||||
Model* model = new Model();
|
Model* model = new Model(nullptr);
|
||||||
model->setIsWireframe(true);
|
model->setIsWireframe(true);
|
||||||
model->init();
|
model->init();
|
||||||
return model;
|
return model;
|
||||||
|
|
|
@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<Net
|
||||||
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
||||||
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||||
|
|
||||||
Model::Model(QObject* parent, RigPointer rig) :
|
Model::Model(RigPointer rig, QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_scale(1.0f, 1.0f, 1.0f),
|
_scale(1.0f, 1.0f, 1.0f),
|
||||||
_scaleToFit(false),
|
_scaleToFit(false),
|
||||||
|
@ -252,22 +252,9 @@ QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) {
|
||||||
};
|
};
|
||||||
|
|
||||||
void Model::initJointTransforms() {
|
void Model::initJointTransforms() {
|
||||||
// compute model transforms
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
int numStates = _jointStates.size();
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
for (int i = 0; i < numStates; ++i) {
|
_rig->initJointTransforms(parentTransform);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::init() {
|
void Model::init() {
|
||||||
|
@ -396,14 +383,8 @@ void Model::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::reset() {
|
void Model::reset() {
|
||||||
if (_jointStates.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
_rig->reset(geometry.joints);
|
||||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
_meshGroupsKnown = false;
|
_meshGroupsKnown = false;
|
||||||
_readyWhenAdded = false; // in case any of our users are using scenes
|
_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
|
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||||
|
@ -436,18 +417,20 @@ bool Model::updateGeometry() {
|
||||||
|
|
||||||
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
|
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
|
||||||
QVector<JointState> newJointStates = createJointStates(newGeometry);
|
QVector<JointState> newJointStates = createJointStates(newGeometry);
|
||||||
if (! _jointStates.isEmpty()) {
|
|
||||||
|
if (! _rig->jointStatesEmpty()) {
|
||||||
// copy the existing joint states
|
// copy the existing joint states
|
||||||
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
|
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
|
||||||
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
|
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 oldIndex = it.value() - 1;
|
||||||
int newIndex = newGeometry.getJointIndex(it.key());
|
int newIndex = newGeometry.getJointIndex(it.key());
|
||||||
if (newIndex != -1) {
|
if (newIndex != -1) {
|
||||||
newJointStates[newIndex].copyState(_jointStates[oldIndex]);
|
newJointStates[newIndex].copyState(_rig->getJointState(oldIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteGeometry();
|
deleteGeometry();
|
||||||
_dilatedTextures.clear();
|
_dilatedTextures.clear();
|
||||||
setGeometry(geometry);
|
setGeometry(geometry);
|
||||||
|
@ -457,7 +440,7 @@ bool Model::updateGeometry() {
|
||||||
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||||
initJointStates(newJointStates);
|
initJointStates(newJointStates);
|
||||||
needToRebuild = true;
|
needToRebuild = true;
|
||||||
} else if (_jointStates.isEmpty()) {
|
} else if (_rig->jointStatesEmpty()) {
|
||||||
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
|
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
|
||||||
if (fbxGeometry.joints.size() > 0) {
|
if (fbxGeometry.joints.size() > 0) {
|
||||||
initJointStates(createJointStates(fbxGeometry));
|
initJointStates(createJointStates(fbxGeometry));
|
||||||
|
@ -493,22 +476,9 @@ bool Model::updateGeometry() {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void Model::initJointStates(QVector<JointState> states) {
|
void Model::initJointStates(QVector<JointState> states) {
|
||||||
_jointStates = states;
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
initJointTransforms();
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
|
_boundingRadius = _rig->initJointStates(states, parentTransform);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
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;
|
return translatedPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Model::getJointState(int index, glm::quat& rotation) const {
|
bool Model::getJointState(int index, glm::quat& rotation) const {
|
||||||
if (index == -1 || index >= _jointStates.size()) {
|
return _rig->getJointStateRotation(index, rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const JointState& state = _jointStates.at(index);
|
|
||||||
rotation = state.getRotationInConstrainedFrame();
|
|
||||||
return !state.rotationIsDefault(rotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
|
bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
|
||||||
if (index == -1 || index >= _jointStates.size()) {
|
return _rig->getVisibleJointState(index, rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const JointState& state = _jointStates.at(index);
|
|
||||||
rotation = state.getVisibleRotationInConstrainedFrame();
|
|
||||||
return !state.rotationIsDefault(rotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::clearJointState(int index) {
|
void Model::clearJointState(int index) {
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
_rig->clearJointState(index);
|
||||||
JointState& state = _jointStates[index];
|
|
||||||
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::clearJointAnimationPriority(int index) {
|
void Model::clearJointAnimationPriority(int index) {
|
||||||
if (_rig) {
|
_rig->clearJointAnimationPriority(index);
|
||||||
_rig->clearJointAnimationPriority(index);
|
|
||||||
} else {
|
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
|
||||||
_jointStates[index]._animationPriority = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||||
if (index != -1 && index < _jointStates.size()) {
|
_rig->setJointState(index, valid, rotation, priority);
|
||||||
JointState& state = _jointStates[index];
|
|
||||||
if (valid) {
|
|
||||||
state.setRotationInConstrainedFrame(rotation, priority);
|
|
||||||
} else {
|
|
||||||
state.restoreRotation(1.0f, priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Model::getParentJointIndex(int jointIndex) const {
|
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 {
|
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// position is in world-frame
|
|
||||||
position = _translation + _rotation * _jointStates[jointIndex].getPosition();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getJointPosition(jointIndex, position);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// position is in model-frame
|
|
||||||
position = extractTranslation(_jointStates[jointIndex].getTransform());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rotation = _rotation * _jointStates[jointIndex].getRotation();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getJointRotation(jointIndex, rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rotation = _jointStates[jointIndex].getRotation();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rotation = _rotation * _jointStates[jointIndex].getRotation();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// position is in world-frame
|
|
||||||
position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
||||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rotation = _rotation * _jointStates[jointIndex].getVisibleRotation();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Model::getJointNames() const {
|
QStringList Model::getJointNames() const {
|
||||||
|
@ -1438,12 +1350,14 @@ void Model::updateClusterMatrices() {
|
||||||
if (_showTrueJointTransforms) {
|
if (_showTrueJointTransforms) {
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(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 {
|
} else {
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(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);
|
handle->simulate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
updateJointState(i);
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
}
|
_rig->simulateInternal(parentTransform);
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
|
||||||
_jointStates[i].resetTransformChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
_shapesAreDirty = !_shapes.isEmpty();
|
_shapesAreDirty = !_shapes.isEmpty();
|
||||||
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
|
glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
|
||||||
for (int i = 0; i < _meshStates.size(); i++) {
|
for (int i = 0; i < _meshStates.size(); i++) {
|
||||||
MeshState& state = _meshStates[i];
|
MeshState& state = _meshStates[i];
|
||||||
|
@ -1474,12 +1384,14 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
if (_showTrueJointTransforms) {
|
if (_showTrueJointTransforms) {
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(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 {
|
} else {
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(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) {
|
void Model::updateJointState(int index) {
|
||||||
JointState& state = _jointStates[index];
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
|
_rig->updateJointState(index, parentTransform);
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
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) {
|
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||||
if (freeLineage.isEmpty()) {
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
return false;
|
if (_rig->setJointPosition(jointIndex, position, rotation, useRotation,
|
||||||
|
lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) {
|
||||||
|
_shapesAreDirty = !_shapes.isEmpty();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (lastFreeIndex == -1) {
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {
|
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 FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
|
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
|
||||||
if (freeLineage.isEmpty()) {
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
return;
|
_rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
_shapesAreDirty = !_shapes.isEmpty();
|
_shapesAreDirty = !_shapes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||||
|
return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage);
|
||||||
foreach (int index, freeLineage) {
|
|
||||||
JointState& state = _jointStates[index];
|
|
||||||
state.restoreRotation(fraction, priority);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Model::getLimbLength(int jointIndex) const {
|
float Model::getLimbLength(int jointIndex) const {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||||
float length = 0.0f;
|
return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::renderJointCollisionShapes(float alpha) {
|
void Model::renderJointCollisionShapes(float alpha) {
|
||||||
|
@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() {
|
||||||
|
|
||||||
void Model::deleteGeometry() {
|
void Model::deleteGeometry() {
|
||||||
_blendedVertexBuffers.clear();
|
_blendedVertexBuffers.clear();
|
||||||
_jointStates.clear();
|
_rig->clearJointStates();
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
clearShapes();
|
clearShapes();
|
||||||
|
|
||||||
|
@ -1945,7 +1638,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
||||||
args, locations);
|
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
|
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||||
|
|
|
@ -64,7 +64,7 @@ public:
|
||||||
|
|
||||||
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
|
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
|
||||||
|
|
||||||
Model(QObject* parent = nullptr, RigPointer rig = nullptr);
|
Model(RigPointer rig, QObject* parent = nullptr);
|
||||||
virtual ~Model();
|
virtual ~Model();
|
||||||
|
|
||||||
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
|
/// 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);
|
const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
|
||||||
|
|
||||||
/// Returns the number of joint states in the model.
|
/// 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.
|
/// Fetches the joint state at the specified index.
|
||||||
/// \return whether or not the joint state is "valid" (that is, non-default)
|
/// \return whether or not the joint state is "valid" (that is, non-default)
|
||||||
|
@ -224,8 +224,8 @@ public:
|
||||||
|
|
||||||
void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
|
void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
|
||||||
|
|
||||||
QVector<JointState>& getJointStates() { return _jointStates; }
|
// QVector<JointState>& getJointStates() { return _rig->getJointStates(); }
|
||||||
const QVector<JointState>& getJointStates() const { return _jointStates; }
|
// const QVector<JointState>& getJointStates() const { return _jointStates; }
|
||||||
|
|
||||||
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
|
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
|
||||||
|
|
||||||
|
@ -260,8 +260,6 @@ protected:
|
||||||
|
|
||||||
bool _showTrueJointTransforms;
|
bool _showTrueJointTransforms;
|
||||||
|
|
||||||
QVector<JointState> _jointStates;
|
|
||||||
|
|
||||||
class MeshState {
|
class MeshState {
|
||||||
public:
|
public:
|
||||||
QVector<glm::mat4> clusterMatrices;
|
QVector<glm::mat4> clusterMatrices;
|
||||||
|
@ -283,8 +281,6 @@ protected:
|
||||||
/// Updates the state of the joint at the specified index.
|
/// Updates the state of the joint at the specified index.
|
||||||
virtual void updateJointState(int index);
|
virtual void updateJointState(int index);
|
||||||
|
|
||||||
virtual void updateVisibleJointStates();
|
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model structure
|
/// \param jointIndex index of joint in model structure
|
||||||
/// \param position position of joint in model-frame
|
/// \param position position of joint in model-frame
|
||||||
/// \param rotation rotation of joint in model-frame
|
/// \param rotation rotation of joint in model-frame
|
||||||
|
|
Loading…
Reference in a new issue