History

JointStates are owned by Rig objects. Model, FaceModel, SkeletonModel call into their Rig pointer to access JointStates.

Seth Alves committed 11 years ago

Commit ee334ff1 parent 47965bc19 files · +549 −622Browse files

Showing the changes to libraries/render-utils/src/Model.cpp (1 of 19 files). Show all

Unified · Split

libraries/render-utils/src/Model.cpp+58−363
@@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<Net · 59 unmodified lines
60static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();60static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
61float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;61float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
62 62
63Model::Model(QObject* parent, RigPointer rig) :63Model::Model(RigPointer rig, QObject* parent) :
64 QObject(parent),64 QObject(parent),
65 _scale(1.0f, 1.0f, 1.0f),65 _scale(1.0f, 1.0f, 1.0f),
66 _scaleToFit(false),66 _scaleToFit(false),
@@ -87,7 +87,7 @@ Model::Model(QObject* parent, RigPointer rig) : · 20 unmodified lines
87 if (_viewState) {87 if (_viewState) {
88 moveToThread(_viewState->getMainThread());88 moveToThread(_viewState->getMainThread());
89 }89 }
90 90
91 setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));91 setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));
92}92}
93 93
@@ -252,22 +252,9 @@ QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) { · 158 unmodified lines
252};252};
253 253
254void Model::initJointTransforms() {254void Model::initJointTransforms() {
255 // compute model transforms255 const FBXGeometry& geometry = _geometry->getFBXGeometry();
256 int numStates = _jointStates.size();256 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
257 for (int i = 0; i < numStates; ++i) {257 _rig->initJointTransforms(parentTransform);
258 JointState& state = _jointStates[i];
259 const FBXJoint& joint = state.getFBXJoint();
260 int parentIndex = joint.parentIndex;
261 if (parentIndex == -1) {
262 const FBXGeometry& geometry = _geometry->getFBXGeometry();
263 // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation)
264 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
265 state.initTransform(parentTransform);
266 } else {
267 const JointState& parentState = _jointStates.at(parentIndex);
268 state.initTransform(parentState.getTransform());
269 }
270 }
271}258}
272 259
273void Model::init() {260void Model::init() {
@@ -396,14 +383,8 @@ void Model::init() { · 122 unmodified lines
396}383}
397 384
398void Model::reset() {385void Model::reset() {
399 if (_jointStates.isEmpty()) {
400 return;
401 }
402 const FBXGeometry& geometry = _geometry->getFBXGeometry();386 const FBXGeometry& geometry = _geometry->getFBXGeometry();
403 for (int i = 0; i < _jointStates.size(); i++) {387 _rig->reset(geometry.joints);
404 _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
405 }
406
407 _meshGroupsKnown = false;388 _meshGroupsKnown = false;
408 _readyWhenAdded = false; // in case any of our users are using scenes389 _readyWhenAdded = false; // in case any of our users are using scenes
409 invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid390 invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
@@ -436,28 +417,30 @@ bool Model::updateGeometry() { · 26 unmodified lines
436 417
437 const FBXGeometry& newGeometry = geometry->getFBXGeometry();418 const FBXGeometry& newGeometry = geometry->getFBXGeometry();
438 QVector<JointState> newJointStates = createJointStates(newGeometry);419 QVector<JointState> newJointStates = createJointStates(newGeometry);
439 if (! _jointStates.isEmpty()) {420
421 if (! _rig->jointStatesEmpty()) {
440 // copy the existing joint states422 // copy the existing joint states
441 const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();423 const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
442 for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();424 for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
443 it != oldGeometry.jointIndices.constEnd(); it++) {425 it != oldGeometry.jointIndices.constEnd(); it++) {
444 int oldIndex = it.value() - 1;426 int oldIndex = it.value() - 1;
445 int newIndex = newGeometry.getJointIndex(it.key());427 int newIndex = newGeometry.getJointIndex(it.key());
446 if (newIndex != -1) {428 if (newIndex != -1) {
447 newJointStates[newIndex].copyState(_jointStates[oldIndex]);429 newJointStates[newIndex].copyState(_rig->getJointState(oldIndex));
448 }430 }
449 }431 }
450 } 432 }
433
451 deleteGeometry();434 deleteGeometry();
452 _dilatedTextures.clear();435 _dilatedTextures.clear();
453 setGeometry(geometry);436 setGeometry(geometry);
454 437
455 _meshGroupsKnown = false;438 _meshGroupsKnown = false;
456 _readyWhenAdded = false; // in case any of our users are using scenes439 _readyWhenAdded = false; // in case any of our users are using scenes
457 invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid440 invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
458 initJointStates(newJointStates);441 initJointStates(newJointStates);
459 needToRebuild = true;442 needToRebuild = true;
460 } else if (_jointStates.isEmpty()) {443 } else if (_rig->jointStatesEmpty()) {
461 const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();444 const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
462 if (fbxGeometry.joints.size() > 0) {445 if (fbxGeometry.joints.size() > 0) {
463 initJointStates(createJointStates(fbxGeometry));446 initJointStates(createJointStates(fbxGeometry));
@@ -493,22 +476,9 @@ bool Model::updateGeometry() { · 29 unmodified lines
493 476
494// virtual477// virtual
495void Model::initJointStates(QVector<JointState> states) {478void Model::initJointStates(QVector<JointState> states) {
496 _jointStates = states;479 const FBXGeometry& geometry = _geometry->getFBXGeometry();
497 initJointTransforms();480 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
498 481 _boundingRadius = _rig->initJointStates(states, parentTransform);
499 int numStates = _jointStates.size();
500 float radius = 0.0f;
501 for (int i = 0; i < numStates; ++i) {
502 float distance = glm::length(_jointStates[i].getPosition());
503 if (distance > radius) {
504 radius = distance;
505 }
506 _jointStates[i].buildConstraint();
507 }
508 for (int i = 0; i < _jointStates.size(); i++) {
509 _jointStates[i].slaveVisibleTransform();
510 }
511 _boundingRadius = radius;
512}482}
513 483
514bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, 484bool 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 { · 555 unmodified lines
1070 return translatedPoint;1040 return translatedPoint;
1071}1041}
1072 1042
1073
1074bool Model::getJointState(int index, glm::quat& rotation) const {1043bool Model::getJointState(int index, glm::quat& rotation) const {
1075 if (index == -1 || index >= _jointStates.size()) {1044 return _rig->getJointStateRotation(index, rotation);
1076 return false;
1077 }
1078 const JointState& state = _jointStates.at(index);
1079 rotation = state.getRotationInConstrainedFrame();
1080 return !state.rotationIsDefault(rotation);
1081}1045}
1082 1046
1083bool Model::getVisibleJointState(int index, glm::quat& rotation) const {1047bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
1084 if (index == -1 || index >= _jointStates.size()) {1048 return _rig->getVisibleJointState(index, rotation);
1085 return false;
1086 }
1087 const JointState& state = _jointStates.at(index);
1088 rotation = state.getVisibleRotationInConstrainedFrame();
1089 return !state.rotationIsDefault(rotation);
1090}1049}
1091 1050
1092void Model::clearJointState(int index) {1051void Model::clearJointState(int index) {
1093 if (index != -1 && index < _jointStates.size()) {1052 _rig->clearJointState(index);
1094 JointState& state = _jointStates[index];
1095 state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
1096 }
1097}1053}
1098 1054
1099void Model::clearJointAnimationPriority(int index) {1055void Model::clearJointAnimationPriority(int index) {
1100 if (_rig) {1056 _rig->clearJointAnimationPriority(index);
1101 _rig->clearJointAnimationPriority(index);
1102 } else {
1103 if (index != -1 && index < _jointStates.size()) {
1104 _jointStates[index]._animationPriority = 0.0f;
1105 }
1106 }
1107}1057}
1108 1058
1109void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {1059void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
1110 if (index != -1 && index < _jointStates.size()) {1060 _rig->setJointState(index, valid, rotation, priority);
1111 JointState& state = _jointStates[index];
1112 if (valid) {
1113 state.setRotationInConstrainedFrame(rotation, priority);
1114 } else {
1115 state.restoreRotation(1.0f, priority);
1116 }
1117 }
1118}1061}
1119 1062
1120int Model::getParentJointIndex(int jointIndex) const {1063int Model::getParentJointIndex(int jointIndex) const {
@@ -1189,62 +1132,31 @@ void Model::setCollisionModelURL(const QUrl& url) { · 68 unmodified lines
1189}1132}
1190 1133
1191bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {1134bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
1192 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1135 return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
1193 return false;
1194 }
1195 // position is in world-frame
1196 position = _translation + _rotation * _jointStates[jointIndex].getPosition();
1197 return true;
1198}1136}
1199 1137
1200bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {1138bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
1201 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1139 return _rig->getJointPosition(jointIndex, position);
1202 return false;
1203 }
1204 // position is in model-frame
1205 position = extractTranslation(_jointStates[jointIndex].getTransform());
1206 return true;
1207}1140}
1208 1141
1209bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {1142bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
1210 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1143 return _rig->getJointRotationInWorldFrame(jointIndex, rotation, _rotation);
1211 return false;
1212 }
1213 rotation = _rotation * _jointStates[jointIndex].getRotation();
1214 return true;
1215}1144}
1216 1145
1217bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {1146bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
1218 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1147 return _rig->getJointRotation(jointIndex, rotation);
1219 return false;
1220 }
1221 rotation = _jointStates[jointIndex].getRotation();
1222 return true;
1223}1148}
1224 1149
1225bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {1150bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
1226 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1151 return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
1227 return false;
1228 }
1229 rotation = _rotation * _jointStates[jointIndex].getRotation();
1230 return true;
1231}1152}
1232 1153
1233bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {1154bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
1234 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1155 return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
1235 return false;
1236 }
1237 // position is in world-frame
1238 position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition();
1239 return true;
1240}1156}
1241 1157
1242bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {1158bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
1243 if (jointIndex == -1 || jointIndex >= _jointStates.size()) {1159 return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation);
1244 return false;
1245 }
1246 rotation = _rotation * _jointStates[jointIndex].getVisibleRotation();
1247 return true;
1248}1160}
1249 1161
1250QStringList Model::getJointNames() const {1162QStringList Model::getJointNames() const {
@@ -1438,12 +1350,14 @@ void Model::updateClusterMatrices() { · 187 unmodified lines
1438 if (_showTrueJointTransforms) {1350 if (_showTrueJointTransforms) {
1439 for (int j = 0; j < mesh.clusters.size(); j++) {1351 for (int j = 0; j < mesh.clusters.size(); j++) {
1440 const FBXCluster& cluster = mesh.clusters.at(j);1352 const FBXCluster& cluster = mesh.clusters.at(j);
1441 state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;1353 state.clusterMatrices[j] =
1354 modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
1442 }1355 }
1443 } else {1356 } else {
1444 for (int j = 0; j < mesh.clusters.size(); j++) {1357 for (int j = 0; j < mesh.clusters.size(); j++) {
1445 const FBXCluster& cluster = mesh.clusters.at(j);1358 const FBXCluster& cluster = mesh.clusters.at(j);
1446 state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;1359 state.clusterMatrices[j] =
1360 modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
1447 }1361 }
1448 }1362 }
1449 }1363 }
@@ -1457,16 +1371,12 @@ void Model::simulateInternal(float deltaTime) { · 7 unmodified lines
1457 handle->simulate(deltaTime);1371 handle->simulate(deltaTime);
1458 }1372 }
1459 1373
1460 for (int i = 0; i < _jointStates.size(); i++) {1374 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1461 updateJointState(i);1375 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1462 }1376 _rig->simulateInternal(parentTransform);
1463 for (int i = 0; i < _jointStates.size(); i++) {
1464 _jointStates[i].resetTransformChanged();
1465 }
1466 1377
1467 _shapesAreDirty = !_shapes.isEmpty();1378 _shapesAreDirty = !_shapes.isEmpty();
1468 1379
1469 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1470 glm::mat4 modelToWorld = glm::mat4_cast(_rotation);1380 glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
1471 for (int i = 0; i < _meshStates.size(); i++) {1381 for (int i = 0; i < _meshStates.size(); i++) {
1472 MeshState& state = _meshStates[i];1382 MeshState& state = _meshStates[i];
@@ -1474,12 +1384,14 @@ void Model::simulateInternal(float deltaTime) { · 1 unmodified line
1474 if (_showTrueJointTransforms) {1384 if (_showTrueJointTransforms) {
1475 for (int j = 0; j < mesh.clusters.size(); j++) {1385 for (int j = 0; j < mesh.clusters.size(); j++) {
1476 const FBXCluster& cluster = mesh.clusters.at(j);1386 const FBXCluster& cluster = mesh.clusters.at(j);
1477 state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;1387 state.clusterMatrices[j] =
1388 modelToWorld * _rig->getJointTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
1478 }1389 }
1479 } else {1390 } else {
1480 for (int j = 0; j < mesh.clusters.size(); j++) {1391 for (int j = 0; j < mesh.clusters.size(); j++) {
1481 const FBXCluster& cluster = mesh.clusters.at(j);1392 const FBXCluster& cluster = mesh.clusters.at(j);
1482 state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;1393 state.clusterMatrices[j] =
1394 modelToWorld * _rig->getJointVisibleTransform(cluster.jointIndex) * cluster.inverseBindMatrix;
1483 }1395 }
1484 }1396 }
1485 }1397 }
@@ -1492,261 +1404,42 @@ void Model::simulateInternal(float deltaTime) { · 6 unmodified lines
1492}1404}
1493 1405
1494void Model::updateJointState(int index) {1406void Model::updateJointState(int index) {
1495 JointState& state = _jointStates[index];1407 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1496 const FBXJoint& joint = state.getFBXJoint();1408 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1497 1409 _rig->updateJointState(index, parentTransform);
1498 // compute model transforms
1499 int parentIndex = joint.parentIndex;
1500 if (parentIndex == -1) {
1501 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1502 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1503 state.computeTransform(parentTransform);
1504 } else {
1505 // guard against out-of-bounds access to _jointStates
1506 if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
1507 const JointState& parentState = _jointStates.at(parentIndex);
1508 state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
1509 }
1510 }
1511}
1512
1513void Model::updateVisibleJointStates() {
1514 if (_showTrueJointTransforms) {
1515 // no need to update visible transforms
1516 return;
1517 }
1518 for (int i = 0; i < _jointStates.size(); i++) {
1519 _jointStates[i].slaveVisibleTransform();
1520 }
1521}1410}
1522 1411
1523bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,1412bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
1524 int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {1413 int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
1525 if (jointIndex == -1 || _jointStates.isEmpty()) {
1526 return false;
1527 }
1528 const FBXGeometry& geometry = _geometry->getFBXGeometry();1414 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1529 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;1415 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
1530 if (freeLineage.isEmpty()) {1416 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1531 return false;1417 if (_rig->setJointPosition(jointIndex, position, rotation, useRotation,
1532 }1418 lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) {
1533 if (lastFreeIndex == -1) {1419 _shapesAreDirty = !_shapes.isEmpty();
1534 lastFreeIndex = freeLineage.last();1420 return true;
1535 }
1536
1537 // this is a cyclic coordinate descent algorithm: see
1538 // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
1539 const int ITERATION_COUNT = 1;
1540 glm::vec3 worldAlignment = alignment;
1541 for (int i = 0; i < ITERATION_COUNT; i++) {
1542 // first, try to rotate the end effector as close as possible to the target rotation, if any
1543 glm::quat endRotation;
1544 if (useRotation) {
1545 JointState& state = _jointStates[jointIndex];
1546
1547 state.setRotationInBindFrame(rotation, priority);
1548 endRotation = state.getRotationInBindFrame();
1549 }
1550
1551 // then, we go from the joint upwards, rotating the end as close as possible to the target
1552 glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform());
1553 for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
1554 int index = freeLineage.at(j);
1555 JointState& state = _jointStates[index];
1556 const FBXJoint& joint = state.getFBXJoint();
1557 if (!(joint.isFree || allIntermediatesFree)) {
1558 continue;
1559 }
1560 glm::vec3 jointPosition = extractTranslation(state.getTransform());
1561 glm::vec3 jointVector = endPosition - jointPosition;
1562 glm::quat oldCombinedRotation = state.getRotation();
1563 glm::quat combinedDelta;
1564 float combinedWeight;
1565 if (useRotation) {
1566 combinedDelta = safeMix(rotation * glm::inverse(endRotation),
1567 rotationBetween(jointVector, position - jointPosition), 0.5f);
1568 combinedWeight = 2.0f;
1569
1570 } else {
1571 combinedDelta = rotationBetween(jointVector, position - jointPosition);
1572 combinedWeight = 1.0f;
1573 }
1574 if (alignment != glm::vec3() && j > 1) {
1575 jointVector = endPosition - jointPosition;
1576 glm::vec3 positionSum;
1577 for (int k = j - 1; k > 0; k--) {
1578 int index = freeLineage.at(k);
1579 updateJointState(index);
1580 positionSum += extractTranslation(_jointStates.at(index).getTransform());
1581 }
1582 glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
1583 glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector));
1584 glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
1585 const float LENGTH_EPSILON = 0.001f;
1586 if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
1587 combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
1588 1.0f / (combinedWeight + 1.0f));
1589 }
1590 }
1591 state.applyRotationDelta(combinedDelta, true, priority);
1592 glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation);
1593 endPosition = actualDelta * jointVector + jointPosition;
1594 if (useRotation) {
1595 endRotation = actualDelta * endRotation;
1596 }
1597 }
1598 }
1599
1600 // now update the joint states from the top
1601 for (int j = freeLineage.size() - 1; j >= 0; j--) {
1602 updateJointState(freeLineage.at(j));
1603 }1421 }
1604 _shapesAreDirty = !_shapes.isEmpty();1422 return false;
1605
1606 return true;
1607}1423}
1608 1424
1609void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {1425void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {
1610 // NOTE: targetRotation is from bind- to model-frame
1611
1612 if (endIndex == -1 || _jointStates.isEmpty()) {
1613 return;
1614 }
1615
1616 const FBXGeometry& geometry = _geometry->getFBXGeometry();1426 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1617 const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;1427 const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
1618 if (freeLineage.isEmpty()) {1428 glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1619 return;1429 _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);
1620 }
1621 int numFree = freeLineage.size();
1622
1623 // store and remember topmost parent transform
1624 glm::mat4 topParentTransform;
1625 {
1626 int index = freeLineage.last();
1627 const JointState& state = _jointStates.at(index);
1628 const FBXJoint& joint = state.getFBXJoint();
1629 int parentIndex = joint.parentIndex;
1630 if (parentIndex == -1) {
1631 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1632 topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
1633 } else {
1634 topParentTransform = _jointStates[parentIndex].getTransform();
1635 }
1636 }
1637
1638 // this is a cyclic coordinate descent algorithm: see
1639 // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
1640
1641 // keep track of the position of the end-effector
1642 JointState& endState = _jointStates[endIndex];
1643 glm::vec3 endPosition = endState.getPosition();
1644 float distanceToGo = glm::distance(targetPosition, endPosition);
1645
1646 const int MAX_ITERATION_COUNT = 2;
1647 const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
1648 int numIterations = 0;
1649 do {
1650 ++numIterations;
1651 // moving up, rotate each free joint to get endPosition closer to target
1652 for (int j = 1; j < numFree; j++) {
1653 int nextIndex = freeLineage.at(j);
1654 JointState& nextState = _jointStates[nextIndex];
1655 FBXJoint nextJoint = nextState.getFBXJoint();
1656 if (! nextJoint.isFree) {
1657 continue;
1658 }
1659
1660 glm::vec3 pivot = nextState.getPosition();
1661 glm::vec3 leverArm = endPosition - pivot;
1662 float leverLength = glm::length(leverArm);
1663 if (leverLength < EPSILON) {
1664 continue;
1665 }
1666 glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
1667
1668 // We want to mix the shortest rotation with one that will pull the system down with gravity
1669 // so that limbs don't float unrealistically. To do this we compute a simplified center of mass
1670 // where each joint has unit mass and we don't bother averaging it because we only need direction.
1671 if (j > 1) {
1672
1673 glm::vec3 centerOfMass(0.0f);
1674 for (int k = 0; k < j; ++k) {
1675 int massIndex = freeLineage.at(k);
1676 centerOfMass += _jointStates[massIndex].getPosition() - pivot;
1677 }
1678 // the gravitational effect is a rotation that tends to align the two cross products
1679 const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f);
1680 glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
1681 glm::cross(worldAlignment, leverArm));
1682
1683 float gravityAngle = glm::angle(gravityDelta);
1684 const float MIN_GRAVITY_ANGLE = 0.1f;
1685 float mixFactor = 0.5f;
1686 if (gravityAngle < MIN_GRAVITY_ANGLE) {
1687 // the final rotation is a mix of the two
1688 mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
1689 }
1690 deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
1691 }
1692
1693 // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
1694 // in the process. This provides stability to the IK solution for most models.
1695 glm::quat oldNextRotation = nextState.getRotation();
1696 float mixFactor = 0.03f;
1697 nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
1698
1699 // measure the result of the rotation which may have been modified by
1700 // blending and constraints
1701 glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
1702 endPosition = pivot + actualDelta * leverArm;
1703 }
1704
1705 // recompute transforms from the top down
1706 glm::mat4 parentTransform = topParentTransform;
1707 for (int j = numFree - 1; j >= 0; --j) {
1708 JointState& freeState = _jointStates[freeLineage.at(j)];
1709 freeState.computeTransform(parentTransform);
1710 parentTransform = freeState.getTransform();
1711 }
1712
1713 // measure our success
1714 endPosition = endState.getPosition();
1715 distanceToGo = glm::distance(targetPosition, endPosition);
1716 } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
1717
1718 // set final rotation of the end joint
1719 endState.setRotationInBindFrame(targetRotation, priority, true);
1720
1721 _shapesAreDirty = !_shapes.isEmpty();1430 _shapesAreDirty = !_shapes.isEmpty();
1722}1431}
1723 1432
1724bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {1433bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
1725 if (jointIndex == -1 || _jointStates.isEmpty()) {
1726 return false;
1727 }
1728 const FBXGeometry& geometry = _geometry->getFBXGeometry();1434 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1729 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;1435 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
1730 1436 return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage);
1731 foreach (int index, freeLineage) {
1732 JointState& state = _jointStates[index];
1733 state.restoreRotation(fraction, priority);
1734 }
1735 return true;
1736}1437}
1737 1438
1738float Model::getLimbLength(int jointIndex) const {1439float Model::getLimbLength(int jointIndex) const {
1739 if (jointIndex == -1 || _jointStates.isEmpty()) {
1740 return 0.0f;
1741 }
1742 const FBXGeometry& geometry = _geometry->getFBXGeometry();1440 const FBXGeometry& geometry = _geometry->getFBXGeometry();
1743 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;1441 const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
1744 float length = 0.0f;1442 return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);
1745 float lengthScale = (_scale.x + _scale.y + _scale.z) / 3.0f;
1746 for (int i = freeLineage.size() - 2; i >= 0; i--) {
1747 length += geometry.joints.at(freeLineage.at(i)).distanceToParent * lengthScale;
1748 }
1749 return length;
1750}1443}
1751 1444
1752void Model::renderJointCollisionShapes(float alpha) {1445void Model::renderJointCollisionShapes(float alpha) {
@@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() { · 63 unmodified lines
1816 1509
1817void Model::deleteGeometry() {1510void Model::deleteGeometry() {
1818 _blendedVertexBuffers.clear();1511 _blendedVertexBuffers.clear();
1819 _jointStates.clear();1512 _rig->clearJointStates();
1820 _meshStates.clear();1513 _meshStates.clear();
1821 clearShapes();1514 clearShapes();
1822 1515
@@ -1945,7 +1638,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran · 122 unmodified lines
1945 args, locations);1638 args, locations);
1946 1639
1947 {1640 {
1948 updateVisibleJointStates();1641 if (!_showTrueJointTransforms) {
1642 _rig->updateVisibleJointStates();
1643 } // else no need to update visible transforms
1949 }1644 }
1950 1645
1951 // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown1646 // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown