Skip to content

Commit

Permalink
NEW: Animation curve related changes:
Browse files Browse the repository at this point in the history
- Quaternions are now flipped to give continuous curves
- added zero animation threshold arguments
- when animation channels are forced, all TRS and weight channels are created now
  • Loading branch information
ziriax committed Nov 18, 2020
1 parent 56bc809 commit 34aa09e
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 201 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# We'll use defaults from the LLVM style, but with 4 columns indentation.
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 120
ColumnLimit: 150
21 changes: 21 additions & 0 deletions src/Arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ const auto forceRootNode = "frn";

const auto forceAnimationChannels = "fac";

const auto forceAnimationSampling = "fas";

const auto hashBufferURIs = "hbu";

const auto dumpAccessorComponents = "dac";
Expand All @@ -82,6 +84,12 @@ const auto reportSkewedInverseBindMatrices = "rsb";
const auto clearOutputWindow = "cow";

const auto cameras = "cam";

const auto constantTranslationThreshold = "ctt";
const auto constantRotationThreshold = "crt";
const auto constantScalingThreshold = "cst";
const auto constantWeightsThreshold = "cwt";

} // namespace flag

inline const char *getArgTypeName(const MSyntax::MArgType argType) {
Expand Down Expand Up @@ -173,6 +181,8 @@ SyntaxFactory::SyntaxFactory() {
registerFlag(ss, flag::bakeScalingFactor, "bakeScalingFactor", kNoArg);
registerFlag(ss, flag::forceRootNode, "forceRootNode", kNoArg);
registerFlag(ss, flag::forceAnimationChannels, "forceAnimationChannels", kNoArg);
registerFlag(ss, flag::forceAnimationSampling, "forceAnimationSampling", kNoArg);

registerFlag(ss, flag::hashBufferURIs, "hashBufferUri", kNoArg);
registerFlag(ss, flag::niceBufferURIs, "niceBufferNames", kNoArg);

Expand All @@ -182,6 +192,11 @@ SyntaxFactory::SyntaxFactory() {

registerFlag(ss, flag::cameras, "cameras", true, kString);

registerFlag(ss, flag::constantTranslationThreshold, "constantTranslationThreshold", kDouble);
registerFlag(ss, flag::constantRotationThreshold, "constantRotationThreshold", kDouble);
registerFlag(ss, flag::constantScalingThreshold, "constantScalingThreshold", kDouble);
registerFlag(ss, flag::constantWeightsThreshold, "constantWeightsThreshold", kDouble);

m_usage = ss.str();
}

Expand Down Expand Up @@ -473,6 +488,7 @@ Arguments::Arguments(const MArgList &args, const MSyntax &syntax) {
bakeScalingFactor = adb.isFlagSet(flag::bakeScalingFactor);
forceRootNode = adb.isFlagSet(flag::forceRootNode);
forceAnimationChannels = adb.isFlagSet(flag::forceAnimationChannels);
forceAnimationSampling = adb.isFlagSet(flag::forceAnimationSampling);
hashBufferURIs = adb.isFlagSet(flag::hashBufferURIs);
niceBufferURIs = adb.isFlagSet(flag::niceBufferURIs);
convertUnsupportedImages = adb.isFlagSet(flag::convertUnsupportedImages);
Expand All @@ -481,6 +497,11 @@ Arguments::Arguments(const MArgList &args, const MSyntax &syntax) {

adb.optional(flag::globalOpacityFactor, opacityFactor);

adb.optional(flag::constantTranslationThreshold, constantTranslationThreshold);
adb.optional(flag::constantRotationThreshold, constantRotationThreshold);
adb.optional(flag::constantScalingThreshold, constantScalingThreshold);
adb.optional(flag::constantWeightsThreshold, constantWeightsThreshold);

if (!adb.optional(flag::sceneName, sceneName)) {
// Use filename without extension of current scene file.
MFileIO fileIO;
Expand Down
18 changes: 16 additions & 2 deletions src/Arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,12 @@ class Arguments {
* which case an extra root node is not always needed? */
bool forceRootNode = false;

/** Force the creation of an animation channel for each node, even if the
* node doesn't contain any animation? */
/** Force the creation of an animation channel for each node, even if the node doesn't contain any animation? */
bool forceAnimationChannels = false;

/** Force the sampling of an animation channel for each node, even if the node doesn't contain any animation? */
bool forceAnimationSampling = false;

/** Use a hash of the buffer for its URI? Useful when exporting the same
* mesh buffer per animation scene */
bool hashBufferURIs = false;
Expand Down Expand Up @@ -232,6 +234,18 @@ class Arguments {
* too. */
bool visibleNodesOnly = false;

/** Consider a translation animation path as constant if all values are below this threshold */
double constantTranslationThreshold = 1e-9;

/** Consider a rotation animation path as constant if all values are below this threshold */
double constantRotationThreshold = 1e-9;

/** Consider a scaling animation path as constant if all values are below this threshold */
double constantScalingThreshold = 1e-9;

/** Consider a blend shape weight animation path as constant if all values are below this threshold */
double constantWeightsThreshold = 1e-9;

std::vector<AnimClipArg> animationClips;

/** Copyright text of the exported file */
Expand Down
29 changes: 10 additions & 19 deletions src/ExportableClip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
#include "progress.h"
#include "timeControl.h"

ExportableClip::ExportableClip(const Arguments &args,
const AnimClipArg &clipArg,
const ExportableScene &scene)
: m_frames(args.makeName(clipArg.name + "/anim/frames"),
clipArg.frameCount(), clipArg.framesPerSecond) {
ExportableClip::ExportableClip(const Arguments &args, const AnimClipArg &clipArg, const ExportableScene &scene)
: m_frames(args.makeName(clipArg.name + "/anim/frames"), clipArg.frameCount(), clipArg.framesPerSecond) {
glAnimation.name = clipArg.name;

const auto frameCount = clipArg.frameCount();
Expand All @@ -21,30 +18,25 @@ ExportableClip::ExportableClip(const Arguments &args,

for (auto &pair : items) {
auto &node = pair.second;
auto nodeAnimation = node->createAnimation(m_frames, scaleFactor);
auto nodeAnimation = node->createAnimation(args, m_frames, scaleFactor);
if (nodeAnimation) {
m_nodeAnimations.emplace_back(std::move(nodeAnimation));
}
}

for (auto relativeFrameIndex = 0; relativeFrameIndex < frameCount;
++relativeFrameIndex) {
for (auto relativeFrameIndex = 0; relativeFrameIndex < frameCount; ++relativeFrameIndex) {
const double relativeFrameTime = m_frames.times.at(relativeFrameIndex);
const MTime absoluteFrameTime =
clipArg.startTime + MTime(relativeFrameTime, MTime::kSeconds);
const MTime absoluteFrameTime = clipArg.startTime + MTime(relativeFrameTime, MTime::kSeconds);
setCurrentTime(absoluteFrameTime, args.redrawViewport);

NodeTransformCache transformCache;
for (auto &nodeAnimation : m_nodeAnimations) {
nodeAnimation->sampleAt(absoluteFrameTime, relativeFrameIndex,
transformCache);
nodeAnimation->sampleAt(absoluteFrameTime, relativeFrameIndex, transformCache);
}

if (relativeFrameIndex % checkProgressFrameInterval ==
checkProgressFrameInterval - 1) {
uiAdvanceProgress(
"exporting clip '" + clipArg.name +
formatted("' %d%%", relativeFrameIndex * 100 / frameCount));
if (relativeFrameIndex % checkProgressFrameInterval == checkProgressFrameInterval - 1) {
uiAdvanceProgress("exporting clip '" + clipArg.name +
formatted("' %d%%", relativeFrameIndex * 100 / frameCount));
}
}

Expand All @@ -55,8 +47,7 @@ ExportableClip::ExportableClip(const Arguments &args,

ExportableClip::~ExportableClip() = default;

void ExportableClip::getAllAccessors(
std::vector<GLTF::Accessor *> &accessors) const {
void ExportableClip::getAllAccessors(std::vector<GLTF::Accessor *> &accessors) const {
m_frames.getAllAccessors(accessors);

for (auto &&animation : m_nodeAnimations) {
Expand Down
3 changes: 1 addition & 2 deletions src/ExportableClip.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

class ExportableClip {
public:
ExportableClip(const Arguments &args, const AnimClipArg &clipArg,
const ExportableScene &scene);
ExportableClip(const Arguments &args, const AnimClipArg &clipArg, const ExportableScene &scene);
virtual ~ExportableClip();

GLTF::Animation glAnimation;
Expand Down
3 changes: 1 addition & 2 deletions src/ExportableItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
ExportableItem::~ExportableItem() = default;

std::unique_ptr<NodeAnimation>
ExportableItem::createAnimation(const ExportableFrames &frameTimes,
const double scaleFactor) {
ExportableItem::createAnimation(const Arguments &, const ExportableFrames &, double ) {
return nullptr;
}
6 changes: 3 additions & 3 deletions src/ExportableItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

class ExportableFrames;
class NodeAnimation;
class Arguments;

class ExportableItem {
public:
virtual ~ExportableItem() = 0;

virtual std::unique_ptr<NodeAnimation>
createAnimation(const ExportableFrames &frameTimes,
const double scaleFactor);
virtual std::unique_ptr<NodeAnimation> createAnimation(const Arguments &args, const ExportableFrames &frameTimes,
double scaleFactor);

protected:
ExportableItem() = default;
Expand Down
60 changes: 20 additions & 40 deletions src/ExportableNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,17 @@
#include "NodeAnimation.h"
#include "Transform.h"

ExportableNode::ExportableNode(const MDagPath &dagPath)
: ExportableObject(dagPath.node()), dagPath(dagPath) {}
ExportableNode::ExportableNode(const MDagPath &dagPath) : ExportableObject(dagPath.node()), dagPath(dagPath) {}

void ExportableNode::load(ExportableScene &scene,
NodeTransformCache &transformCache) {
void ExportableNode::load(ExportableScene &scene, NodeTransformCache &transformCache) {
MStatus status;

auto &resources = scene.resources();
auto &args = resources.arguments();

m_disableNameAssignment = args.disableNameAssignment;
m_forceAnimationChannels = args.forceAnimationChannels;

// Is this a joint with segment scale compensation? (the default in Maya)
bool maybeSegmentScaleCompensation = false;
DagHelper::getPlugValue(obj, "segmentScaleCompensate",
maybeSegmentScaleCompensation);
DagHelper::getPlugValue(obj, "segmentScaleCompensate", maybeSegmentScaleCompensation);

// Remember scale factor
scaleFactor = args.getBakeScaleFactor();
Expand All @@ -40,8 +34,7 @@ void ExportableNode::load(ExportableScene &scene,
// Deal with segment scale compensation
// A root joint never has segment scale compensation, since the parent is
// the world.
if (maybeSegmentScaleCompensation && parentNode &&
parentNode->obj.hasFn(MFn::kJoint) &&
if (maybeSegmentScaleCompensation && parentNode && parentNode->obj.hasFn(MFn::kJoint) &&
!args.ignoreSegmentScaleCompensation) {
transformKind = TransformKind::ComplexJoint;
}
Expand All @@ -56,12 +49,10 @@ void ExportableNode::load(ExportableScene &scene,
const auto rotatePivot = fnTransform.rotatePivot(MSpace::kObject);

if (scalePivot != rotatePivot) {
MayaException::printError(
formatted(
"Transform '%s' has different scaling and rotation pivots, "
"this is not supported, ignoring scaling pivot!",
dagPath.partialPathName().asChar()),
MStatus::kNotImplemented);
MayaException::printError(formatted("Transform '%s' has different scaling and rotation pivots, "
"this is not supported, ignoring scaling pivot!",
dagPath.partialPathName().asChar()),
MStatus::kNotImplemented);
}

pivotPoint = rotatePivot;
Expand Down Expand Up @@ -123,8 +114,7 @@ void ExportableNode::load(ExportableScene &scene,
<< "' has initial transforms that are not representable by glTF! "
"Skewing is not supported, use 3 nodes to simulate this. "
"Deviation = "
<< std::fixed << std::setprecision(2)
<< initialTransformState.maxNonOrthogonality * 100 << "%" << endl;
<< std::fixed << std::setprecision(2) << initialTransformState.maxNonOrthogonality * 100 << "%" << endl;
}

// Create mesh, if any
Expand All @@ -135,8 +125,7 @@ void ExportableNode::load(ExportableScene &scene,

if (status && shapeDagPath.hasFn(MFn::kMesh)) {
// The shape is a mesh
m_mesh =
std::make_unique<ExportableMesh>(scene, *this, shapeDagPath);
m_mesh = std::make_unique<ExportableMesh>(scene, *this, shapeDagPath);
m_mesh->attachToNode(pNode);
}
}
Expand All @@ -148,8 +137,7 @@ void ExportableNode::load(ExportableScene &scene,

if (status && shapeDagPath.hasFn(MFn::kCamera)) {
// The shape is a camera
m_camera =
std::make_unique<ExportableCamera>(scene, *this, shapeDagPath);
m_camera = std::make_unique<ExportableCamera>(scene, *this, shapeDagPath);
m_camera->attachToNode(pNode);
}
}
Expand All @@ -158,11 +146,9 @@ void ExportableNode::load(ExportableScene &scene,
ExportableNode::~ExportableNode() = default;

std::unique_ptr<NodeAnimation>
ExportableNode::createAnimation(const ExportableFrames &frameTimes,
const double scaleFactor) {
return std::make_unique<NodeAnimation>(*this, frameTimes, scaleFactor,
m_disableNameAssignment,
m_forceAnimationChannels);
ExportableNode::createAnimation(const Arguments &args, const ExportableFrames &frameTimes, const double scaleFactor) {

return std::make_unique<NodeAnimation>(*this, frameTimes, scaleFactor, args);
}

void ExportableNode::updateNodeTransforms(NodeTransformCache &transformCache) {
Expand All @@ -175,12 +161,10 @@ void ExportableNode::updateNodeTransforms(NodeTransformCache &transformCache) {
// and scale matrices.
const auto currentFrameTime = MAnimControl::currentTime();

cerr << prefix << "WARNING: node '" << name()
<< "' has transforms at the current frame " << currentFrameTime
cerr << prefix << "WARNING: node '" << name() << "' has transforms at the current frame " << currentFrameTime
<< " that are not representable by glTF! Skewing is not "
"supported, use 3 nodes to simulate this. Deviation = "
<< std::fixed << std::setprecision(2)
<< currentTransformState.maxNonOrthogonality * 100 << "%" << endl;
<< std::fixed << std::setprecision(2) << currentTransformState.maxNonOrthogonality * 100 << "%" << endl;
}
}

Expand Down Expand Up @@ -211,17 +195,14 @@ bool ExportableNode::tryMergeRedundantShapeNode() {
return false;

auto *trs = static_cast<const GLTF::Node::TransformTRS *>(transform);
if (trs->translation[0] != 0 || trs->translation[1] != 0 ||
trs->translation[2] != 0)
if (trs->translation[0] != 0 || trs->translation[1] != 0 || trs->translation[2] != 0)
return false;
if (trs->rotation[0] != 0 || trs->rotation[1] != 0 ||
trs->rotation[2] != 0 || trs->rotation[3] != 1)
if (trs->rotation[0] != 0 || trs->rotation[1] != 0 || trs->rotation[2] != 0 || trs->rotation[3] != 1)
return false;
if (trs->scale[0] != 1 || trs->scale[1] != 1 || trs->scale[2] != 1)
return false;

cout << prefix << "Shape-only node '" << name()
<< "' is redundant, moving its shapes to parent node '"
cout << prefix << "Shape-only node '" << name() << "' is redundant, moving its shapes to parent node '"
<< parentNode->name() << "'" << endl;

glParentNode.children.clear();
Expand All @@ -242,8 +223,7 @@ bool ExportableNode::tryMergeRedundantShapeNode() {
return true;
}

void ExportableNode::getAllAccessors(
std::vector<GLTF::Accessor *> &accessors) const {
void ExportableNode::getAllAccessors(std::vector<GLTF::Accessor *> &accessors) const {
if (m_mesh) {
m_mesh->getAllAccessors(accessors);
}
Expand Down
24 changes: 6 additions & 18 deletions src/ExportableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,21 @@ class ExportableNode : public ExportableObject {
NodeTransformState initialTransformState;
NodeTransformState currentTransformState;

std::unique_ptr<NodeAnimation>
createAnimation(const ExportableFrames &frameTimes,
const double scaleFactor) override;
std::unique_ptr<NodeAnimation> createAnimation(const Arguments &args, const ExportableFrames &frameTimes,
double scaleFactor) override;

// The primary node to represent the transform
// See Transform.h for details
GLTF::Node &glPrimaryNode() { return m_glNodes[0]; }
const GLTF::Node &glPrimaryNode() const {
return const_cast<ExportableNode *>(this)->glPrimaryNode();
}
const GLTF::Node &glPrimaryNode() const { return const_cast<ExportableNode *>(this)->glPrimaryNode(); }

// The secondary node to represent the transform
// Can be the same as the primary node for simple transforms
// See Transform.h for details
GLTF::Node &glSecondaryNode() {
return m_glNodes[transformKind != TransformKind::Simple];
}
const GLTF::Node &glSecondaryNode() const {
return const_cast<ExportableNode *>(this)->glSecondaryNode();
}
GLTF::Node &glSecondaryNode() { return m_glNodes[transformKind != TransformKind::Simple]; }
const GLTF::Node &glSecondaryNode() const { return const_cast<ExportableNode *>(this)->glSecondaryNode(); }

MDagPath parentDagPath() const {
return parentNode ? parentNode->dagPath : MDagPath();
}
MDagPath parentDagPath() const { return parentNode ? parentNode->dagPath : MDagPath(); }

// Update the node transforms using the values at the current frame
void updateNodeTransforms(NodeTransformCache &transformCache);
Expand All @@ -88,8 +79,5 @@ class ExportableNode : public ExportableObject {
std::unique_ptr<ExportableMesh> m_mesh;
std::unique_ptr<ExportableCamera> m_camera;

bool m_disableNameAssignment = false;
bool m_forceAnimationChannels = false;

DISALLOW_COPY_MOVE_ASSIGN(ExportableNode);
};
Loading

0 comments on commit 34aa09e

Please sign in to comment.