diff --git a/docs/examples/modeling-mosquito/mosquito.ds3 b/docs/examples/modeling-mosquito/mosquito.ds3
index c32ec05b..567a628c 100644
--- a/docs/examples/modeling-mosquito/mosquito.ds3
+++ b/docs/examples/modeling-mosquito/mosquito.ds3
@@ -1,116 +1,117 @@
DUST3D 1.0 xml 0000000193
-
-
+
+
‰PNG
diff --git a/dust3d.pro b/dust3d.pro
index e1666491..ed017c2e 100644
--- a/dust3d.pro
+++ b/dust3d.pro
@@ -143,6 +143,9 @@ HEADERS += src/jointnodetree.h
SOURCES += src/animationclipgenerator.cpp
HEADERS += src/animationclipgenerator.h
+SOURCES += src/animationclipplayer.cpp
+HEADERS += src/animationclipplayer.h
+
SOURCES += src/skinnedmesh.cpp
HEADERS += src/skinnedmesh.h
diff --git a/src/animationclipgenerator.cpp b/src/animationclipgenerator.cpp
index 9cbdff15..12226864 100644
--- a/src/animationclipgenerator.cpp
+++ b/src/animationclipgenerator.cpp
@@ -2,14 +2,35 @@
#include "animationclipgenerator.h"
#include "skinnedmesh.h"
-AnimationClipGenerator::AnimationClipGenerator(const MeshResultContext &resultContext,
- const QString &motionName, const std::map ¶meters) :
+const std::vector AnimationClipGenerator::supportedClipNames = {
+ "Idle",
+ //"Walk",
+ //"Run",
+ //"Attack",
+ //"Hurt",
+ //"Die",
+};
+
+AnimationClipGenerator::AnimationClipGenerator(const MeshResultContext &resultContext, const JointNodeTree &jointNodeTree,
+ const QString &clipName, const std::map ¶meters, bool wantMesh) :
m_resultContext(resultContext),
- m_motionName(motionName),
- m_parameters(parameters)
+ m_jointNodeTree(jointNodeTree),
+ m_clipName(clipName),
+ m_parameters(parameters),
+ m_wantMesh(wantMesh)
{
}
+const std::vector &AnimationClipGenerator::times()
+{
+ return m_times;
+}
+
+const std::vector &AnimationClipGenerator::frames()
+{
+ return m_frames;
+}
+
AnimationClipGenerator::~AnimationClipGenerator()
{
for (auto &mesh: m_frameMeshes) {
@@ -17,26 +38,48 @@ AnimationClipGenerator::~AnimationClipGenerator()
}
}
-std::vector> AnimationClipGenerator::takeFrameMeshes()
+std::vector> AnimationClipGenerator::takeFrameMeshes()
{
- std::vector> result = m_frameMeshes;
+ std::vector> result = m_frameMeshes;
m_frameMeshes.clear();
return result;
}
+void AnimationClipGenerator::generateFrame(SkinnedMesh &skinnedMesh, float amount, float beginTime, float duration)
+{
+ RigController *rigController = skinnedMesh.rigController();
+ JointNodeTree *jointNodeTree = skinnedMesh.jointNodeTree();
+
+ rigController->resetFrame();
+
+ if (m_clipName == "Idle")
+ rigController->idle(amount);
+
+ RigFrame frame(jointNodeTree->joints().size());
+ rigController->saveFrame(frame);
+
+ if (m_wantMesh) {
+ skinnedMesh.applyRigFrameToMesh(frame);
+ m_frameMeshes.push_back(std::make_pair(duration, skinnedMesh.toMeshLoader()));
+ }
+
+ m_times.push_back(beginTime);
+ m_frames.push_back(frame);
+}
+
void AnimationClipGenerator::generate()
{
- SkinnedMesh skinnedMesh(m_resultContext);
+ SkinnedMesh skinnedMesh(m_resultContext, m_jointNodeTree);
skinnedMesh.startRig();
-
- RigController *rigController = skinnedMesh.rigController();
-
- for (float amount = 0.0; amount <= 0.5; amount += 0.05) {
- rigController->squat(amount);
- RigFrame frame;
- rigController->saveFrame(frame);
- skinnedMesh.applyRigFrameToMesh(frame);
- m_frameMeshes.push_back(std::make_pair(10, skinnedMesh.toMeshLoader()));
+ float duration = 0.1;
+ float nextBeginTime = 0;
+ for (float amount = 0.0; amount <= 0.05; amount += 0.01) {
+ generateFrame(skinnedMesh, amount, nextBeginTime, duration);
+ nextBeginTime += duration;
+ }
+ for (float amount = 0.05; amount >= 0.0; amount -= 0.01) {
+ generateFrame(skinnedMesh, amount, nextBeginTime, duration);
+ nextBeginTime += duration;
}
}
diff --git a/src/animationclipgenerator.h b/src/animationclipgenerator.h
index a8ee70d3..8227df56 100644
--- a/src/animationclipgenerator.h
+++ b/src/animationclipgenerator.h
@@ -5,6 +5,8 @@
#include
#include "meshresultcontext.h"
#include "meshloader.h"
+#include "skinnedmesh.h"
+#include "jointnodetree.h"
class AnimationClipGenerator : public QObject
{
@@ -14,16 +16,25 @@ signals:
public slots:
void process();
public:
- AnimationClipGenerator(const MeshResultContext &resultContext,
- const QString &motionName, const std::map ¶meters);
+ AnimationClipGenerator(const MeshResultContext &resultContext, const JointNodeTree &jointNodeTree,
+ const QString &clipName, const std::map ¶meters, bool wantMesh=true);
~AnimationClipGenerator();
- std::vector> takeFrameMeshes();
+ std::vector> takeFrameMeshes();
+ const std::vector ×();
+ const std::vector &frames();
void generate();
+ static const std::vector supportedClipNames;
+private:
+ void generateFrame(SkinnedMesh &skinnedMesh, float amount, float beginTime, float duration);
private:
MeshResultContext m_resultContext;
- QString m_motionName;
+ JointNodeTree m_jointNodeTree;
+ QString m_clipName;
std::map m_parameters;
- std::vector> m_frameMeshes;
+ bool m_wantMesh = true;
+ std::vector> m_frameMeshes;
+ std::vector m_times;
+ std::vector m_frames;
};
#endif
diff --git a/src/animationclipplayer.cpp b/src/animationclipplayer.cpp
new file mode 100644
index 00000000..aad3a3ad
--- /dev/null
+++ b/src/animationclipplayer.cpp
@@ -0,0 +1,59 @@
+#include "animationclipplayer.h"
+
+AnimationClipPlayer::~AnimationClipPlayer()
+{
+ clear();
+}
+
+void AnimationClipPlayer::updateFrameMeshes(std::vector> &frameMeshes)
+{
+ clear();
+
+ m_frameMeshes = frameMeshes;
+ frameMeshes.clear();
+
+ m_currentPlayIndex = 0;
+ m_countForFrame.restart();
+
+ if (!m_frameMeshes.empty())
+ m_timerForFrame.singleShot(0, this, &AnimationClipPlayer::frameReadyToShow);
+}
+
+void AnimationClipPlayer::clear()
+{
+ freeFrames();
+ delete m_lastFrameMesh;
+ m_lastFrameMesh = nullptr;
+}
+
+void AnimationClipPlayer::freeFrames()
+{
+ for (auto &it: m_frameMeshes) {
+ delete it.second;
+ }
+ m_frameMeshes.clear();
+}
+
+MeshLoader *AnimationClipPlayer::takeFrameMesh()
+{
+ if (m_currentPlayIndex >= (int)m_frameMeshes.size()) {
+ if (nullptr != m_lastFrameMesh)
+ return new MeshLoader(*m_lastFrameMesh);
+ return nullptr;
+ }
+ int millis = m_frameMeshes[m_currentPlayIndex].first * 1000 - m_countForFrame.elapsed();
+ if (millis > 0) {
+ m_timerForFrame.singleShot(millis, this, &AnimationClipPlayer::frameReadyToShow);
+ if (nullptr != m_lastFrameMesh)
+ return new MeshLoader(*m_lastFrameMesh);
+ return nullptr;
+ }
+ m_currentPlayIndex = (m_currentPlayIndex + 1) % m_frameMeshes.size();
+ m_countForFrame.restart();
+
+ MeshLoader *mesh = new MeshLoader(*m_frameMeshes[m_currentPlayIndex].second);
+ m_timerForFrame.singleShot(m_frameMeshes[m_currentPlayIndex].first * 1000, this, &AnimationClipPlayer::frameReadyToShow);
+ delete m_lastFrameMesh;
+ m_lastFrameMesh = new MeshLoader(*mesh);
+ return mesh;
+}
diff --git a/src/animationclipplayer.h b/src/animationclipplayer.h
new file mode 100644
index 00000000..095d9742
--- /dev/null
+++ b/src/animationclipplayer.h
@@ -0,0 +1,29 @@
+#ifndef ANIMATION_PLAYER_H
+#define ANIMATION_PLAYER_H
+#include
+#include
+#include
+#include "meshloader.h"
+
+class AnimationClipPlayer : public QObject
+{
+ Q_OBJECT
+signals:
+ void frameReadyToShow();
+public:
+ ~AnimationClipPlayer();
+ MeshLoader *takeFrameMesh();
+ void updateFrameMeshes(std::vector> &frameMeshes);
+ void clear();
+private:
+ void freeFrames();
+private:
+ MeshLoader *m_lastFrameMesh = nullptr;
+ int m_currentPlayIndex = 0;
+private:
+ std::vector> m_frameMeshes;
+ QTime m_countForFrame;
+ QTimer m_timerForFrame;
+};
+
+#endif
diff --git a/src/animationpanelwidget.cpp b/src/animationpanelwidget.cpp
index e5531c19..1fc753ce 100644
--- a/src/animationpanelwidget.cpp
+++ b/src/animationpanelwidget.cpp
@@ -6,98 +6,78 @@
AnimationPanelWidget::AnimationPanelWidget(SkeletonDocument *document, QWidget *parent) :
QWidget(parent),
m_document(document),
- m_animationClipGenerator(nullptr),
- m_lastFrameMesh(nullptr),
- m_sourceMeshReady(false)
+ m_animationClipGenerator(nullptr)
{
- QHBoxLayout *moveControlButtonLayout = new QHBoxLayout;
- QHBoxLayout *fightControlButtonLayout = new QHBoxLayout;
+ connect(&m_clipPlayer, &AnimationClipPlayer::frameReadyToShow, this, &AnimationPanelWidget::frameReadyToShow);
+
+ QVBoxLayout *buttonsLayout = new QVBoxLayout;
+ buttonsLayout->addStretch();
QPushButton *resetButton = new QPushButton(tr("Reset"));
- connect(resetButton, &QPushButton::clicked, [=] {
- m_lastMotionName.clear();
- emit panelClosed();
- });
+ connect(resetButton, &QPushButton::clicked, this, &AnimationPanelWidget::reset);
+ buttonsLayout->addWidget(resetButton);
- QPushButton *walkButton = new QPushButton(tr("Walk"));
- connect(walkButton, &QPushButton::clicked, [=] {
- generateClip("Walk");
- });
+ buttonsLayout->addSpacing(10);
- QPushButton *runButton = new QPushButton(tr("Run"));
- connect(runButton, &QPushButton::clicked, [=] {
- generateClip("Run");
- });
-
- QPushButton *attackButton = new QPushButton(tr("Attack"));
- connect(attackButton, &QPushButton::clicked, [=] {
- generateClip("Attack");
- });
-
- QPushButton *hurtButton = new QPushButton(tr("Hurt"));
- connect(hurtButton, &QPushButton::clicked, [=] {
- generateClip("Hurt");
- });
-
- QPushButton *dieButton = new QPushButton(tr("Die"));
- connect(dieButton, &QPushButton::clicked, [=] {
- generateClip("Die");
- });
-
- moveControlButtonLayout->addStretch();
- moveControlButtonLayout->addWidget(resetButton);
- moveControlButtonLayout->addWidget(walkButton);
- moveControlButtonLayout->addWidget(runButton);
- moveControlButtonLayout->addStretch();
-
- fightControlButtonLayout->addStretch();
- fightControlButtonLayout->addWidget(attackButton);
- fightControlButtonLayout->addWidget(hurtButton);
- fightControlButtonLayout->addWidget(dieButton);
- fightControlButtonLayout->addStretch();
+ for (const auto &clipName: AnimationClipGenerator::supportedClipNames) {
+ QPushButton *clipButton = new QPushButton(QObject::tr(qPrintable(clipName)));
+ connect(clipButton, &QPushButton::clicked, [=] {
+ generateClip(clipName);
+ });
+ buttonsLayout->addWidget(clipButton);
+ }
+ buttonsLayout->addStretch();
QVBoxLayout *controlLayout = new QVBoxLayout;
controlLayout->setSpacing(0);
controlLayout->setContentsMargins(0, 0, 0, 0);
- controlLayout->addLayout(moveControlButtonLayout);
- controlLayout->addLayout(fightControlButtonLayout);
+ controlLayout->addLayout(buttonsLayout);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(controlLayout);
setLayout(mainLayout);
- m_countForFrame.start();
+ setMinimumWidth(200);
setWindowTitle(APP_NAME);
}
AnimationPanelWidget::~AnimationPanelWidget()
{
- delete m_lastFrameMesh;
- for (auto &it: m_frameMeshes) {
- delete it.second;
- }
}
void AnimationPanelWidget::sourceMeshChanged()
{
- m_sourceMeshReady = true;
- if (m_nextMotionName.isEmpty())
+ if (m_nextMotionName.isEmpty() && m_lastMotionName.isEmpty())
return;
-
- generateClip(m_nextMotionName);
+
+ if (!m_nextMotionName.isEmpty()) {
+ generateClip(m_nextMotionName);
+ return;
+ }
+
+ if (!m_lastMotionName.isEmpty()) {
+ generateClip(m_lastMotionName);
+ return;
+ }
}
void AnimationPanelWidget::hideEvent(QHideEvent *event)
+{
+ reset();
+}
+
+void AnimationPanelWidget::reset()
{
m_lastMotionName.clear();
+ m_clipPlayer.clear();
emit panelClosed();
}
void AnimationPanelWidget::generateClip(QString motionName)
{
- if (nullptr != m_animationClipGenerator || !m_sourceMeshReady) {
+ if (nullptr != m_animationClipGenerator) {
m_nextMotionName = motionName;
return;
}
@@ -110,7 +90,8 @@ void AnimationPanelWidget::generateClip(QString motionName)
std::map parameters;
m_animationClipGenerator = new AnimationClipGenerator(m_document->currentPostProcessedResultContext(),
- m_nextMotionName, parameters);
+ m_document->currentJointNodeTree(),
+ motionName, parameters, true);
m_animationClipGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_animationClipGenerator, &AnimationClipGenerator::process);
connect(m_animationClipGenerator, &AnimationClipGenerator::finished, this, &AnimationPanelWidget::clipReady);
@@ -121,16 +102,12 @@ void AnimationPanelWidget::generateClip(QString motionName)
void AnimationPanelWidget::clipReady()
{
- m_frameMeshes = m_animationClipGenerator->takeFrameMeshes();
+ auto frameMeshes = m_animationClipGenerator->takeFrameMeshes();
+ m_clipPlayer.updateFrameMeshes(frameMeshes);
delete m_animationClipGenerator;
m_animationClipGenerator = nullptr;
-
- m_countForFrame.restart();
-
- if (!m_frameMeshes.empty())
- QTimer::singleShot(m_frameMeshes[0].first, this, &AnimationPanelWidget::frameReadyToShow);
-
+
qDebug() << "Animation clip generation done";
if (!m_nextMotionName.isEmpty())
@@ -139,28 +116,5 @@ void AnimationPanelWidget::clipReady()
MeshLoader *AnimationPanelWidget::takeFrameMesh()
{
- if (m_lastMotionName.isEmpty())
- return m_document->takeResultMesh();
-
- if (m_frameMeshes.empty()) {
- if (nullptr != m_lastFrameMesh)
- return new MeshLoader(*m_lastFrameMesh);
- return nullptr;
- }
- int millis = m_frameMeshes[0].first - m_countForFrame.elapsed();
- if (millis > 0) {
- QTimer::singleShot(millis, this, &AnimationPanelWidget::frameReadyToShow);
- if (nullptr != m_lastFrameMesh)
- return new MeshLoader(*m_lastFrameMesh);
- return nullptr;
- }
- MeshLoader *mesh = m_frameMeshes[0].second;
- m_frameMeshes.erase(m_frameMeshes.begin());
- m_countForFrame.restart();
- if (!m_frameMeshes.empty()) {
- QTimer::singleShot(m_frameMeshes[0].first, this, &AnimationPanelWidget::frameReadyToShow);
- }
- delete m_lastFrameMesh;
- m_lastFrameMesh = new MeshLoader(*mesh);
- return mesh;
+ return m_clipPlayer.takeFrameMesh();
}
diff --git a/src/animationpanelwidget.h b/src/animationpanelwidget.h
index 1bcf928f..09fdbc4f 100644
--- a/src/animationpanelwidget.h
+++ b/src/animationpanelwidget.h
@@ -5,6 +5,7 @@
#include
#include "skeletondocument.h"
#include "animationclipgenerator.h"
+#include "animationclipplayer.h"
class AnimationPanelWidget : public QWidget
{
@@ -22,14 +23,13 @@ public slots:
void generateClip(QString motionName);
void clipReady();
void sourceMeshChanged();
+private:
+ void reset();
private:
SkeletonDocument *m_document;
AnimationClipGenerator *m_animationClipGenerator;
- MeshLoader *m_lastFrameMesh;
- bool m_sourceMeshReady;
private:
- std::vector> m_frameMeshes;
- QTime m_countForFrame;
+ AnimationClipPlayer m_clipPlayer;
QString m_nextMotionName;
QString m_lastMotionName;
};
diff --git a/src/ccdikresolver.cpp b/src/ccdikresolver.cpp
index aa75915b..a63a9b09 100644
--- a/src/ccdikresolver.cpp
+++ b/src/ccdikresolver.cpp
@@ -6,8 +6,8 @@
CCDIKSolver::CCDIKSolver() :
m_maxRound(4),
- m_distanceThreshold2(0.01 * 0.01),
- m_distanceCeaseThreshold2(0.01 * 0.01)
+ m_distanceThreshold2(0.001 * 0.001),
+ m_distanceCeaseThreshold2(0.001 * 0.001)
{
}
diff --git a/src/dust3dutil.cpp b/src/dust3dutil.cpp
index 70bd8347..782ff152 100644
--- a/src/dust3dutil.cpp
+++ b/src/dust3dutil.cpp
@@ -26,4 +26,3 @@ void qNormalizeAngle(int &angle)
while (angle > 360 * 16)
angle -= 360 * 16;
}
-
diff --git a/src/dust3dutil.h b/src/dust3dutil.h
index 208a6625..cbbc80a1 100644
--- a/src/dust3dutil.h
+++ b/src/dust3dutil.h
@@ -4,6 +4,7 @@
#include