Showing the changes to libraries/render-utils/src/Model.cpp (1 of 19 files). Show all
Unified · Split
| @@ -60,7 +60,7 @@ static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<QWeakPointer<Net · 59 unmodified lines | |||
| 60 | static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >(); | 60 | static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >(); |
| 61 | float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; | 61 | float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; |
| 62 | 62 | ||
| 63 | Model::Model(QObject* parent, RigPointer rig) : | 63 | Model::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 | ||
| 254 | void Model::initJointTransforms() { | 254 | void Model::initJointTransforms() { |
| 255 | // compute model transforms | 255 | 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 | ||
| 273 | void Model::init() { | 260 | void Model::init() { |
| @@ -396,14 +383,8 @@ void Model::init() { · 122 unmodified lines | |||
| 396 | } | 383 | } |
| 397 | 384 | ||
| 398 | void Model::reset() { | 385 | void 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 scenes | 389 | _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 invalid | 390 | 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 states | 422 | // 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 scenes | 439 | _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 invalid | 440 | 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 | // virtual | 477 | // virtual |
| 495 | void Model::initJointStates(QVector<JointState> states) { | 478 | void 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 | ||
| 514 | bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, | 484 | 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 { · 555 unmodified lines | |||
| 1070 | return translatedPoint; | 1040 | return translatedPoint; |
| 1071 | } | 1041 | } |
| 1072 | 1042 | ||
| 1073 | |||
| 1074 | bool Model::getJointState(int index, glm::quat& rotation) const { | 1043 | bool 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 | ||
| 1083 | bool Model::getVisibleJointState(int index, glm::quat& rotation) const { | 1047 | bool 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 | ||
| 1092 | void Model::clearJointState(int index) { | 1051 | void 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 | ||
| 1099 | void Model::clearJointAnimationPriority(int index) { | 1055 | void 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 | ||
| 1109 | void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { | 1059 | void 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 | ||
| 1120 | int Model::getParentJointIndex(int jointIndex) const { | 1063 | int Model::getParentJointIndex(int jointIndex) const { |
| @@ -1189,62 +1132,31 @@ void Model::setCollisionModelURL(const QUrl& url) { · 68 unmodified lines | |||
| 1189 | } | 1132 | } |
| 1190 | 1133 | ||
| 1191 | bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { | 1134 | bool 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 | ||
| 1200 | bool Model::getJointPosition(int jointIndex, glm::vec3& position) const { | 1138 | bool 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 | ||
| 1209 | bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { | 1142 | bool 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 | ||
| 1217 | bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { | 1146 | bool 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 | ||
| 1225 | bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { | 1150 | bool 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 | ||
| 1233 | bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { | 1154 | bool 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 | ||
| 1242 | bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { | 1158 | bool 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 | ||
| 1250 | QStringList Model::getJointNames() const { | 1162 | QStringList 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 | ||
| 1494 | void Model::updateJointState(int index) { | 1406 | void 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 | |||
| 1513 | void 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 | ||
| 1523 | bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, | 1412 | bool 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 | ||
| 1609 | void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { | 1425 | void 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 | ||
| 1724 | bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { | 1433 | bool 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 | ||
| 1738 | float Model::getLimbLength(int jointIndex) const { | 1439 | float 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 | ||
| 1752 | void Model::renderJointCollisionShapes(float alpha) { | 1445 | void Model::renderJointCollisionShapes(float alpha) { |
| @@ -1816,7 +1509,7 @@ void Model::applyNextGeometry() { · 63 unmodified lines | |||
| 1816 | 1509 | ||
| 1817 | void Model::deleteGeometry() { | 1510 | void 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 _meshGroupsKnown | 1646 | // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown |