Fix pose editor
parent
9f12f03d58
commit
839e081d10
|
@ -29,6 +29,23 @@ void JointNodeTree::reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JointNodeTree::calculateBonePositions(std::vector<std::pair<QVector3D, QVector3D>> *bonePositions,
|
||||||
|
const std::vector<RiggerBone> *rigBones) const
|
||||||
|
{
|
||||||
|
if (nullptr == bonePositions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*bonePositions).resize(m_boneNodes.size());
|
||||||
|
for (int i = 0; i < (int)m_boneNodes.size(); i++) {
|
||||||
|
const auto &node = m_boneNodes[i];
|
||||||
|
QVector3D headPosition = node.transformMatrix * node.position;
|
||||||
|
QVector3D tailPosition = node.transformMatrix * (node.position + ((*rigBones)[i].tailPosition - (*rigBones)[i].headPosition));
|
||||||
|
if (-1 != node.parentIndex)
|
||||||
|
(*bonePositions)[node.parentIndex].second = headPosition;
|
||||||
|
(*bonePositions)[i] = std::make_pair(headPosition, tailPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JointNodeTree::recalculateTransformMatrices()
|
void JointNodeTree::recalculateTransformMatrices()
|
||||||
{
|
{
|
||||||
for (decltype(m_boneNodes.size()) i = 0; i < m_boneNodes.size(); i++) {
|
for (decltype(m_boneNodes.size()) i = 0; i < m_boneNodes.size(); i++) {
|
||||||
|
|
|
@ -28,6 +28,8 @@ public:
|
||||||
void addTranslation(int index, QVector3D translation);
|
void addTranslation(int index, QVector3D translation);
|
||||||
void reset();
|
void reset();
|
||||||
void recalculateTransformMatrices();
|
void recalculateTransformMatrices();
|
||||||
|
void calculateBonePositions(std::vector<std::pair<QVector3D, QVector3D>> *bonePositions,
|
||||||
|
const std::vector<RiggerBone> *rigBones) const;
|
||||||
static JointNodeTree slerp(const JointNodeTree &first, const JointNodeTree &second, float t);
|
static JointNodeTree slerp(const JointNodeTree &first, const JointNodeTree &second, float t);
|
||||||
private:
|
private:
|
||||||
std::vector<JointNode> m_boneNodes;
|
std::vector<JointNode> m_boneNodes;
|
||||||
|
|
|
@ -103,14 +103,16 @@ float MotionsGenerator::calculatePoseDuration(const QUuid &poseId)
|
||||||
return totalDuration;
|
return totalDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::pair<float, JointNodeTree>> &MotionsGenerator::getProceduralAnimation(ProceduralAnimation proceduralAnimation)
|
const std::vector<std::pair<float, JointNodeTree>> &MotionsGenerator::getProceduralAnimation(ProceduralAnimation proceduralAnimation,
|
||||||
|
const JointNodeTree *initialJointNodeTree)
|
||||||
{
|
{
|
||||||
auto findResult = m_proceduralAnimations.find((int)proceduralAnimation);
|
auto findResult = m_proceduralAnimations.find((int)proceduralAnimation);
|
||||||
if (findResult != m_proceduralAnimations.end())
|
if (findResult != m_proceduralAnimations.end())
|
||||||
return findResult->second;
|
return findResult->second;
|
||||||
std::vector<std::pair<float, JointNodeTree>> &resultFrames = m_proceduralAnimations[(int)proceduralAnimation];
|
std::vector<std::pair<float, JointNodeTree>> &resultFrames = m_proceduralAnimations[(int)proceduralAnimation];
|
||||||
|
if (ProceduralAnimation::FallToDeath == proceduralAnimation) {
|
||||||
//std::vector<MeshLoader *> &resultPreviews = m_proceduralPreviews[(int)proceduralAnimation];
|
//std::vector<MeshLoader *> &resultPreviews = m_proceduralPreviews[(int)proceduralAnimation];
|
||||||
RagDoll ragdoll(&m_rigBones);
|
RagDoll ragdoll(&m_rigBones, initialJointNodeTree);
|
||||||
float stepSeconds = 1.0 / 60;
|
float stepSeconds = 1.0 / 60;
|
||||||
float maxSeconds = 1.5;
|
float maxSeconds = 1.5;
|
||||||
int maxSteps = maxSeconds / stepSeconds;
|
int maxSteps = maxSeconds / stepSeconds;
|
||||||
|
@ -121,12 +123,18 @@ const std::vector<std::pair<float, JointNodeTree>> &MotionsGenerator::getProcedu
|
||||||
//resultPreviews.push_back(preview);
|
//resultPreviews.push_back(preview);
|
||||||
++steps;
|
++steps;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (resultFrames.empty()) {
|
||||||
|
resultFrames.push_back(std::make_pair(0, JointNodeTree(&m_rigBones)));
|
||||||
|
}
|
||||||
return resultFrames;
|
return resultFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MotionsGenerator::calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation)
|
float MotionsGenerator::calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation,
|
||||||
|
const JointNodeTree *initialJointNodeTree)
|
||||||
{
|
{
|
||||||
const auto &result = getProceduralAnimation(proceduralAnimation);
|
const auto &result = getProceduralAnimation(proceduralAnimation,
|
||||||
|
initialJointNodeTree);
|
||||||
float totalDuration = 0;
|
float totalDuration = 0;
|
||||||
for (const auto &it: result) {
|
for (const auto &it: result) {
|
||||||
totalDuration += it.first;
|
totalDuration += it.first;
|
||||||
|
@ -145,14 +153,20 @@ float MotionsGenerator::calculateMotionDuration(const QUuid &motionId, std::set<
|
||||||
}
|
}
|
||||||
float totalDuration = 0;
|
float totalDuration = 0;
|
||||||
visited.insert(motionId);
|
visited.insert(motionId);
|
||||||
for (const auto &clip: *motionClips) {
|
for (int clipIndex = 0; clipIndex < (int)(*motionClips).size(); ++clipIndex) {
|
||||||
|
const auto &clip = (*motionClips)[clipIndex];
|
||||||
if (clip.clipType == MotionClipType::Interpolation)
|
if (clip.clipType == MotionClipType::Interpolation)
|
||||||
totalDuration += clip.duration;
|
totalDuration += clip.duration;
|
||||||
else if (clip.clipType == MotionClipType::Pose)
|
else if (clip.clipType == MotionClipType::Pose)
|
||||||
totalDuration += calculatePoseDuration(clip.linkToId);
|
totalDuration += calculatePoseDuration(clip.linkToId);
|
||||||
else if (clip.clipType == MotionClipType::ProceduralAnimation)
|
else if (clip.clipType == MotionClipType::ProceduralAnimation) {
|
||||||
totalDuration += calculateProceduralAnimationDuration(clip.proceduralAnimation);
|
const JointNodeTree *previousJointNodeTree = nullptr;
|
||||||
else if (clip.clipType == MotionClipType::Motion)
|
if (clipIndex > 1) {
|
||||||
|
previousJointNodeTree = findClipEndJointNodeTree((*motionClips)[clipIndex - 2]);
|
||||||
|
}
|
||||||
|
totalDuration += calculateProceduralAnimationDuration(clip.proceduralAnimation,
|
||||||
|
previousJointNodeTree);
|
||||||
|
} else if (clip.clipType == MotionClipType::Motion)
|
||||||
totalDuration += calculateMotionDuration(clip.linkToId, visited);
|
totalDuration += calculateMotionDuration(clip.linkToId, visited);
|
||||||
}
|
}
|
||||||
return totalDuration;
|
return totalDuration;
|
||||||
|
@ -173,14 +187,20 @@ void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &vi
|
||||||
|
|
||||||
std::vector<float> timePoints;
|
std::vector<float> timePoints;
|
||||||
float totalDuration = 0;
|
float totalDuration = 0;
|
||||||
for (auto &clip: *motionClips) {
|
for (int clipIndex = 0; clipIndex < (int)(*motionClips).size(); ++clipIndex) {
|
||||||
|
auto &clip = (*motionClips)[clipIndex];
|
||||||
if (clip.clipType == MotionClipType::Motion) {
|
if (clip.clipType == MotionClipType::Motion) {
|
||||||
std::set<QUuid> subVisited;
|
std::set<QUuid> subVisited;
|
||||||
clip.duration = calculateMotionDuration(clip.linkToId, subVisited);
|
clip.duration = calculateMotionDuration(clip.linkToId, subVisited);
|
||||||
} else if (clip.clipType == MotionClipType::Pose) {
|
} else if (clip.clipType == MotionClipType::Pose) {
|
||||||
clip.duration = calculatePoseDuration(clip.linkToId);
|
clip.duration = calculatePoseDuration(clip.linkToId);
|
||||||
} else if (clip.clipType == MotionClipType::ProceduralAnimation) {
|
} else if (clip.clipType == MotionClipType::ProceduralAnimation) {
|
||||||
clip.duration = calculateProceduralAnimationDuration(clip.proceduralAnimation);
|
const JointNodeTree *previousJointNodeTree = nullptr;
|
||||||
|
if (clipIndex > 1) {
|
||||||
|
previousJointNodeTree = findClipEndJointNodeTree((*motionClips)[clipIndex - 2]);
|
||||||
|
}
|
||||||
|
clip.duration = calculateProceduralAnimationDuration(clip.proceduralAnimation,
|
||||||
|
previousJointNodeTree);
|
||||||
}
|
}
|
||||||
timePoints.push_back(totalDuration);
|
timePoints.push_back(totalDuration);
|
||||||
totalDuration += clip.duration;
|
totalDuration += clip.duration;
|
||||||
|
@ -220,11 +240,16 @@ void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &vi
|
||||||
qDebug() << "findClipEndJointNodeTree failed";
|
qDebug() << "findClipEndJointNodeTree failed";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const JointNodeTree *endJointNodeTree = findClipBeginJointNodeTree((*motionClips)[clipIndex + 1]);
|
const JointNodeTree *endJointNodeTree = nullptr;
|
||||||
|
if (MotionClipType::ProceduralAnimation == (*motionClips)[clipIndex + 1].clipType) {
|
||||||
|
endJointNodeTree = beginJointNodeTree;
|
||||||
|
} else {
|
||||||
|
endJointNodeTree = findClipBeginJointNodeTree((*motionClips)[clipIndex + 1]);
|
||||||
if (nullptr == endJointNodeTree) {
|
if (nullptr == endJointNodeTree) {
|
||||||
qDebug() << "findClipBeginJointNodeTree failed";
|
qDebug() << "findClipBeginJointNodeTree failed";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
outcomes.push_back({progress - lastProgress,
|
outcomes.push_back({progress - lastProgress,
|
||||||
generateInterpolation(progressClip.interpolationType, *beginJointNodeTree, *endJointNodeTree, clipLocalProgress / std::max((float)0.0001, progressClip.duration))});
|
generateInterpolation(progressClip.interpolationType, *beginJointNodeTree, *endJointNodeTree, clipLocalProgress / std::max((float)0.0001, progressClip.duration))});
|
||||||
lastProgress = progress;
|
lastProgress = progress;
|
||||||
|
@ -258,7 +283,12 @@ void MotionsGenerator::generateMotion(const QUuid &motionId, std::set<QUuid> &vi
|
||||||
progress += progressClip.duration;
|
progress += progressClip.duration;
|
||||||
continue;
|
continue;
|
||||||
} else if (MotionClipType::ProceduralAnimation == progressClip.clipType) {
|
} else if (MotionClipType::ProceduralAnimation == progressClip.clipType) {
|
||||||
const auto &frames = getProceduralAnimation(progressClip.proceduralAnimation);
|
const JointNodeTree *beginJointNodeTree = nullptr;
|
||||||
|
if (clipIndex > 0) {
|
||||||
|
beginJointNodeTree = findClipEndJointNodeTree((*motionClips)[clipIndex - 1]);
|
||||||
|
}
|
||||||
|
const auto &frames = getProceduralAnimation(progressClip.proceduralAnimation,
|
||||||
|
beginJointNodeTree);
|
||||||
float clipDuration = std::max((float)0.0001, progressClip.duration);
|
float clipDuration = std::max((float)0.0001, progressClip.duration);
|
||||||
int frame = clipLocalProgress * frames.size() / clipDuration;
|
int frame = clipLocalProgress * frames.size() / clipDuration;
|
||||||
if (frame >= (int)frames.size())
|
if (frame >= (int)frames.size())
|
||||||
|
|
|
@ -45,8 +45,10 @@ private:
|
||||||
void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews);
|
void generatePreviewsForOutcomes(const std::vector<std::pair<float, JointNodeTree>> &outcomes, std::vector<std::pair<float, MeshLoader *>> &previews);
|
||||||
float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited);
|
float calculateMotionDuration(const QUuid &motionId, std::set<QUuid> &visited);
|
||||||
float calculatePoseDuration(const QUuid &poseId);
|
float calculatePoseDuration(const QUuid &poseId);
|
||||||
float calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation);
|
float calculateProceduralAnimationDuration(ProceduralAnimation proceduralAnimation,
|
||||||
const std::vector<std::pair<float, JointNodeTree>> &getProceduralAnimation(ProceduralAnimation proceduralAnimation);
|
const JointNodeTree *initialJointNodeTree=nullptr);
|
||||||
|
const std::vector<std::pair<float, JointNodeTree>> &getProceduralAnimation(ProceduralAnimation proceduralAnimation,
|
||||||
|
const JointNodeTree *initialJointNodeTree=nullptr);
|
||||||
|
|
||||||
RigType m_rigType = RigType::None;
|
RigType m_rigType = RigType::None;
|
||||||
std::vector<RiggerBone> m_rigBones;
|
std::vector<RiggerBone> m_rigBones;
|
||||||
|
|
|
@ -367,6 +367,7 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
||||||
node.setY(fromOutcomeY(bone.headPosition.y()));
|
node.setY(fromOutcomeY(bone.headPosition.y()));
|
||||||
node.setZ(fromOutcomeZ(bone.headPosition.z()));
|
node.setZ(fromOutcomeZ(bone.headPosition.z()));
|
||||||
nodeMap[node.id] = node;
|
nodeMap[node.id] = node;
|
||||||
|
qDebug() << "Add first node:" << (*rigBones)[edgePair.first].name;
|
||||||
newAddedNodeIds.insert(node.id);
|
newAddedNodeIds.insert(node.id);
|
||||||
boneIndexToHeadNodeIdMap[edgePair.first] = node.id;
|
boneIndexToHeadNodeIdMap[edgePair.first] = node.id;
|
||||||
firstNodeId = node.id;
|
firstNodeId = node.id;
|
||||||
|
@ -376,6 +377,7 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
||||||
}
|
}
|
||||||
auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second);
|
auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second);
|
||||||
if (findSecond == boneIndexToHeadNodeIdMap.end()) {
|
if (findSecond == boneIndexToHeadNodeIdMap.end()) {
|
||||||
|
const auto &firstBone = (*rigBones)[edgePair.first];
|
||||||
const auto &bone = (*rigBones)[edgePair.second];
|
const auto &bone = (*rigBones)[edgePair.second];
|
||||||
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
|
if (!bone.name.startsWith("Virtual_") || !m_hideRootAndVirtual) {
|
||||||
SkeletonNode node;
|
SkeletonNode node;
|
||||||
|
@ -383,10 +385,11 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
||||||
node.id = QUuid::createUuid();
|
node.id = QUuid::createUuid();
|
||||||
partMap[node.partId].nodeIds.push_back(node.id);
|
partMap[node.partId].nodeIds.push_back(node.id);
|
||||||
node.setRadius(m_nodeRadius);
|
node.setRadius(m_nodeRadius);
|
||||||
node.setX(fromOutcomeX(bone.headPosition.x()));
|
node.setX(fromOutcomeX(firstBone.tailPosition.x()));
|
||||||
node.setY(fromOutcomeY(bone.headPosition.y()));
|
node.setY(fromOutcomeY(firstBone.tailPosition.y()));
|
||||||
node.setZ(fromOutcomeZ(bone.headPosition.z()));
|
node.setZ(fromOutcomeZ(firstBone.tailPosition.z()));
|
||||||
nodeMap[node.id] = node;
|
nodeMap[node.id] = node;
|
||||||
|
qDebug() << "Add second node:" << (*rigBones)[edgePair.second].name;
|
||||||
newAddedNodeIds.insert(node.id);
|
newAddedNodeIds.insert(node.id);
|
||||||
boneIndexToHeadNodeIdMap[edgePair.second] = node.id;
|
boneIndexToHeadNodeIdMap[edgePair.second] = node.id;
|
||||||
secondNodeId = node.id;
|
secondNodeId = node.id;
|
||||||
|
@ -395,8 +398,9 @@ void PoseDocument::parametersToNodes(const std::vector<RiggerBone> *rigBones,
|
||||||
secondNodeId = findSecond->second;
|
secondNodeId = findSecond->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstNodeId.isNull() || secondNodeId.isNull())
|
if (firstNodeId.isNull() || secondNodeId.isNull()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (firstNodeSide != secondNodeSide) {
|
if (firstNodeSide != secondNodeSide) {
|
||||||
qDebug() << "First node side:" << SkeletonSideToDispName(firstNodeSide) << "is different with second node side:" << SkeletonSideToDispName(secondNodeSide);
|
qDebug() << "First node side:" << SkeletonSideToDispName(firstNodeSide) << "is different with second node side:" << SkeletonSideToDispName(secondNodeSide);
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
#include "ragdoll.h"
|
#include "ragdoll.h"
|
||||||
#include "poser.h"
|
#include "poser.h"
|
||||||
|
|
||||||
RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones) :
|
RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones,
|
||||||
|
const JointNodeTree *initialJointNodeTree) :
|
||||||
m_jointNodeTree(rigBones),
|
m_jointNodeTree(rigBones),
|
||||||
m_stepJointNodeTree(rigBones)
|
m_stepJointNodeTree(rigBones)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,18 @@ RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones) :
|
||||||
|
|
||||||
m_bones = *rigBones;
|
m_bones = *rigBones;
|
||||||
|
|
||||||
|
std::vector<std::pair<QVector3D, QVector3D>> bonePositions;
|
||||||
|
if (nullptr != initialJointNodeTree) {
|
||||||
|
m_jointNodeTree.calculateBonePositions(&bonePositions,
|
||||||
|
rigBones);
|
||||||
|
} else {
|
||||||
|
bonePositions.resize(m_bones.size());
|
||||||
|
for (size_t i = 0; i < m_bones.size(); ++i) {
|
||||||
|
const auto &bone = m_bones[i];
|
||||||
|
bonePositions[i] = std::make_pair(bone.headPosition, bone.tailPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &bone: m_bones) {
|
for (const auto &bone: m_bones) {
|
||||||
auto radius = qMax(bone.headRadius, bone.tailRadius);
|
auto radius = qMax(bone.headRadius, bone.tailRadius);
|
||||||
m_stepBonePositions.push_back(std::make_tuple(bone.headPosition, bone.tailPosition, radius));
|
m_stepBonePositions.push_back(std::make_tuple(bone.headPosition, bone.tailPosition, radius));
|
||||||
|
@ -44,9 +57,11 @@ RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones) :
|
||||||
Poser::fetchChains(boneNames, m_chains);
|
Poser::fetchChains(boneNames, m_chains);
|
||||||
|
|
||||||
for (const auto &bone: *rigBones) {
|
for (const auto &bone: *rigBones) {
|
||||||
|
const auto &headPosition = bonePositions[bone.index].first;
|
||||||
|
const auto &tailPosition = bonePositions[bone.index].second;
|
||||||
float radius = (bone.headRadius + bone.tailRadius) * 0.5;
|
float radius = (bone.headRadius + bone.tailRadius) * 0.5;
|
||||||
float height = bone.headPosition.distanceToPoint(bone.tailPosition);
|
float height = headPosition.distanceToPoint(tailPosition);
|
||||||
QVector3D middlePosition = (bone.headPosition + bone.tailPosition) * 0.5;
|
QVector3D middlePosition = (headPosition + tailPosition) * 0.5;
|
||||||
m_boneLengthMap[bone.name] = height;
|
m_boneLengthMap[bone.name] = height;
|
||||||
m_boneRadiusMap[bone.name] = radius;
|
m_boneRadiusMap[bone.name] = radius;
|
||||||
m_boneMiddleMap[bone.name] = middlePosition;
|
m_boneMiddleMap[bone.name] = middlePosition;
|
||||||
|
@ -55,6 +70,8 @@ RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones) :
|
||||||
std::set<std::pair<QString, QString>> constraintPairs;
|
std::set<std::pair<QString, QString>> constraintPairs;
|
||||||
|
|
||||||
for (const auto &bone: m_bones) {
|
for (const auto &bone: m_bones) {
|
||||||
|
const auto &headPosition = bonePositions[bone.index].first;
|
||||||
|
const auto &tailPosition = bonePositions[bone.index].second;
|
||||||
float height = m_boneLengthMap[bone.name];
|
float height = m_boneLengthMap[bone.name];
|
||||||
float radius = m_boneRadiusMap[bone.name];
|
float radius = m_boneRadiusMap[bone.name];
|
||||||
float mass = 1.0;
|
float mass = 1.0;
|
||||||
|
@ -72,7 +89,7 @@ RagDoll::RagDoll(const std::vector<RiggerBone> *rigBones) :
|
||||||
btScalar(middlePosition.y()),
|
btScalar(middlePosition.y()),
|
||||||
btScalar(middlePosition.z())
|
btScalar(middlePosition.z())
|
||||||
));
|
));
|
||||||
QVector3D to = (bone.tailPosition - bone.headPosition).normalized();
|
QVector3D to = (tailPosition - headPosition).normalized();
|
||||||
QVector3D from = QVector3D(0, 1, 0);
|
QVector3D from = QVector3D(0, 1, 0);
|
||||||
QQuaternion rotation = QQuaternion::rotationTo(from, to);
|
QQuaternion rotation = QQuaternion::rotationTo(from, to);
|
||||||
btQuaternion btRotation(btScalar(rotation.x()), btScalar(rotation.y()), btScalar(rotation.z()),
|
btQuaternion btRotation(btScalar(rotation.x()), btScalar(rotation.y()), btScalar(rotation.z()),
|
||||||
|
|
|
@ -23,7 +23,8 @@ class RagDoll : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
RagDoll(const std::vector<RiggerBone> *rigBones);
|
RagDoll(const std::vector<RiggerBone> *rigBones,
|
||||||
|
const JointNodeTree *initialJointNodeTree=nullptr);
|
||||||
~RagDoll();
|
~RagDoll();
|
||||||
bool stepSimulation(float amount);
|
bool stepSimulation(float amount);
|
||||||
const JointNodeTree &getStepJointNodeTree();
|
const JointNodeTree &getStepJointNodeTree();
|
||||||
|
|
Loading…
Reference in New Issue