|
@@ -1400,196 +1400,211 @@ private:
|
|
double max_time = -1e10;
|
|
double max_time = -1e10;
|
|
|
|
|
|
try {
|
|
try {
|
|
-
|
|
|
|
- NodeMap node_property_map;
|
|
|
|
BOOST_FOREACH(const NodeMap::value_type& kv, node_map) {
|
|
BOOST_FOREACH(const NodeMap::value_type& kv, node_map) {
|
|
- node_property_map.clear();
|
|
|
|
|
|
+ GenerateNodeAnimations(node_anims,
|
|
|
|
+ kv.first,
|
|
|
|
+ kv.second,
|
|
|
|
+ layer_map,
|
|
|
|
+ min_time,
|
|
|
|
+ max_time);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ catch(std::exception&) {
|
|
|
|
+ std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
|
|
|
|
+ throw;
|
|
|
|
+ }
|
|
|
|
|
|
- ai_assert(kv.second.size());
|
|
|
|
|
|
+ if(node_anims.size()) {
|
|
|
|
+ anim->mChannels = new aiNodeAnim*[node_anims.size()]();
|
|
|
|
+ anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
|
|
|
|
|
|
- const AnimationCurveNode* curve_node;
|
|
|
|
- BOOST_FOREACH(const AnimationCurveNode* node, kv.second) {
|
|
|
|
- ai_assert(node);
|
|
|
|
|
|
+ std::swap_ranges(node_anims.begin(),node_anims.end(),anim->mChannels);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ // empty animations would fail validation, so drop them
|
|
|
|
+ delete anim;
|
|
|
|
+ animations.pop_back();
|
|
|
|
+ FBXImporter::LogInfo("ignoring empty AnimationStack: " + name);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- if (node->TargetProperty().empty()) {
|
|
|
|
- FBXImporter::LogWarn("target property for animation curve not set");
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
|
|
+ // for some mysterious reason, mDuration is simply the maximum key -- the
|
|
|
|
+ // validator always assumes animations to start at zero.
|
|
|
|
+ anim->mDuration = max_time /*- min_time */;
|
|
|
|
+ anim->mTicksPerSecond = 1000.0;
|
|
|
|
+ }
|
|
|
|
|
|
- curve_node = node;
|
|
|
|
- if (node->Curves().empty()) {
|
|
|
|
- FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode");
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- node_property_map[node->TargetProperty()].push_back(node);
|
|
|
|
- }
|
|
|
|
|
|
+ // ------------------------------------------------------------------------------------------------
|
|
|
|
+ void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
|
|
|
|
+ const std::string& fixed_name,
|
|
|
|
+ const std::vector<const AnimationCurveNode*>& curves,
|
|
|
|
+ const LayerMap& layer_map,
|
|
|
|
+ double& max_time,
|
|
|
|
+ double& min_time)
|
|
|
|
+ {
|
|
|
|
|
|
- ai_assert(curve_node);
|
|
|
|
|
|
+ NodeMap node_property_map;
|
|
|
|
+ ai_assert(curves.size());
|
|
|
|
|
|
- // check for all possible transformation components
|
|
|
|
- NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
|
|
|
|
|
|
+ const AnimationCurveNode* curve_node;
|
|
|
|
+ BOOST_FOREACH(const AnimationCurveNode* node, curves) {
|
|
|
|
+ ai_assert(node);
|
|
|
|
|
|
- bool has_any = false;
|
|
|
|
- bool has_complex = false;
|
|
|
|
|
|
+ if (node->TargetProperty().empty()) {
|
|
|
|
+ FBXImporter::LogWarn("target property for animation curve not set");
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
- for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
|
|
|
- const TransformationComp comp = static_cast<TransformationComp>(i);
|
|
|
|
|
|
+ curve_node = node;
|
|
|
|
+ if (node->Curves().empty()) {
|
|
|
|
+ FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode");
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
- // inverse pivots don't exist in the input, we just generate them
|
|
|
|
- if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
|
|
|
|
- chain[i] = node_property_map.end();
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
|
|
+ node_property_map[node->TargetProperty()].push_back(node);
|
|
|
|
+ }
|
|
|
|
|
|
- chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
|
|
|
|
- if (chain[i] != node_property_map.end()) {
|
|
|
|
- has_any = true;
|
|
|
|
|
|
+ ai_assert(curve_node);
|
|
|
|
|
|
- if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
|
|
|
|
- comp != TransformationComp_Translation) {
|
|
|
|
|
|
+ // check for all possible transformation components
|
|
|
|
+ NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
|
|
|
|
|
|
- has_complex = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ bool has_any = false;
|
|
|
|
+ bool has_complex = false;
|
|
|
|
|
|
- if (!has_any) {
|
|
|
|
- FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
|
|
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
|
|
|
+ const TransformationComp comp = static_cast<TransformationComp>(i);
|
|
|
|
|
|
- ai_assert(curve_node->TargetAsModel());
|
|
|
|
|
|
+ // inverse pivots don't exist in the input, we just generate them
|
|
|
|
+ if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
|
|
|
|
+ chain[i] = node_property_map.end();
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
- // this needs to play nicely with GenerateTransformationNodeChain() which will
|
|
|
|
- // be invoked _later_ (animations come first). If this node has only rotation,
|
|
|
|
- // scaling and translation _and_ there are no animated other components either,
|
|
|
|
- // we can use a single node and also a single node animation channel.
|
|
|
|
- const Model& target = *curve_node->TargetAsModel();
|
|
|
|
- if (!has_complex && !NeedsComplexTransformationChain(target)) {
|
|
|
|
|
|
+ chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
|
|
|
|
+ if (chain[i] != node_property_map.end()) {
|
|
|
|
+ has_any = true;
|
|
|
|
|
|
- aiNodeAnim* const nd = GenerateSimpleNodeAnim(kv.first, target, chain,
|
|
|
|
- node_property_map.end(),
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time
|
|
|
|
- );
|
|
|
|
|
|
+ if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
|
|
|
|
+ comp != TransformationComp_Translation) {
|
|
|
|
|
|
- ai_assert(nd);
|
|
|
|
- node_anims.push_back(nd);
|
|
|
|
- continue;
|
|
|
|
|
|
+ has_complex = true;
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- // otherwise, things get gruesome and we need separate animation channels
|
|
|
|
- // for each part of the transformation chain.
|
|
|
|
- for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
|
|
|
- const TransformationComp comp = static_cast<TransformationComp>(i);
|
|
|
|
-
|
|
|
|
- if (chain[i] != node_property_map.end()) {
|
|
|
|
-
|
|
|
|
- const std::string& chain_name = NameTransformationChainNode(kv.first, comp);
|
|
|
|
-
|
|
|
|
- aiNodeAnim* na;
|
|
|
|
- switch(comp)
|
|
|
|
- {
|
|
|
|
- case TransformationComp_Rotation:
|
|
|
|
- case TransformationComp_PreRotation:
|
|
|
|
- case TransformationComp_PostRotation:
|
|
|
|
- na = GenerateRotationNodeAnim(chain_name,
|
|
|
|
- target,
|
|
|
|
- (*chain[i]).second,
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time
|
|
|
|
- );
|
|
|
|
- break;
|
|
|
|
|
|
+ if (!has_any) {
|
|
|
|
+ FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- case TransformationComp_RotationOffset:
|
|
|
|
- case TransformationComp_RotationPivot:
|
|
|
|
- case TransformationComp_ScalingOffset:
|
|
|
|
- case TransformationComp_ScalingPivot:
|
|
|
|
- case TransformationComp_Translation:
|
|
|
|
- na = GenerateTranslationNodeAnim(chain_name,
|
|
|
|
- target,
|
|
|
|
- (*chain[i]).second,
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time);
|
|
|
|
-
|
|
|
|
- // pivoting requires us to generate an inverse channel to undo the pivot translation
|
|
|
|
- if (comp == TransformationComp_RotationPivot) {
|
|
|
|
- const std::string& invName = NameTransformationChainNode(kv.first, TransformationComp_RotationPivotInverse);
|
|
|
|
- aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
|
|
|
- target,
|
|
|
|
- (*chain[i]).second,
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time,
|
|
|
|
- true);
|
|
|
|
-
|
|
|
|
- ai_assert(inv);
|
|
|
|
- node_anims.push_back(inv);
|
|
|
|
- }
|
|
|
|
- else if (comp == TransformationComp_ScalingPivot) {
|
|
|
|
- const std::string& invName = NameTransformationChainNode(kv.first, TransformationComp_ScalingPivotInverse);
|
|
|
|
- aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
|
|
|
- target,
|
|
|
|
- (*chain[i]).second,
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time,
|
|
|
|
- true);
|
|
|
|
-
|
|
|
|
- ai_assert(inv);
|
|
|
|
- node_anims.push_back(inv);
|
|
|
|
- }
|
|
|
|
|
|
+ ai_assert(curve_node->TargetAsModel());
|
|
|
|
|
|
- break;
|
|
|
|
|
|
+ // this needs to play nicely with GenerateTransformationNodeChain() which will
|
|
|
|
+ // be invoked _later_ (animations come first). If this node has only rotation,
|
|
|
|
+ // scaling and translation _and_ there are no animated other components either,
|
|
|
|
+ // we can use a single node and also a single node animation channel.
|
|
|
|
+ const Model& target = *curve_node->TargetAsModel();
|
|
|
|
+ if (!has_complex && !NeedsComplexTransformationChain(target)) {
|
|
|
|
|
|
- case TransformationComp_Scaling:
|
|
|
|
- na = GenerateScalingNodeAnim(chain_name,
|
|
|
|
- target,
|
|
|
|
- (*chain[i]).second,
|
|
|
|
- layer_map,
|
|
|
|
- max_time,
|
|
|
|
- min_time
|
|
|
|
- );
|
|
|
|
- break;
|
|
|
|
|
|
+ aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
|
|
|
|
+ node_property_map.end(),
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time
|
|
|
|
+ );
|
|
|
|
|
|
- default:
|
|
|
|
- ai_assert(false);
|
|
|
|
- }
|
|
|
|
|
|
+ ai_assert(nd);
|
|
|
|
+ node_anims.push_back(nd);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
- ai_assert(na);
|
|
|
|
- node_anims.push_back(na);
|
|
|
|
- continue;
|
|
|
|
|
|
+ // otherwise, things get gruesome and we need separate animation channels
|
|
|
|
+ // for each part of the transformation chain.
|
|
|
|
+ for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
|
|
|
+ const TransformationComp comp = static_cast<TransformationComp>(i);
|
|
|
|
+
|
|
|
|
+ if (chain[i] != node_property_map.end()) {
|
|
|
|
+
|
|
|
|
+ const std::string& chain_name = NameTransformationChainNode(fixed_name, comp);
|
|
|
|
+
|
|
|
|
+ aiNodeAnim* na;
|
|
|
|
+ switch(comp)
|
|
|
|
+ {
|
|
|
|
+ case TransformationComp_Rotation:
|
|
|
|
+ case TransformationComp_PreRotation:
|
|
|
|
+ case TransformationComp_PostRotation:
|
|
|
|
+ na = GenerateRotationNodeAnim(chain_name,
|
|
|
|
+ target,
|
|
|
|
+ (*chain[i]).second,
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time
|
|
|
|
+ );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case TransformationComp_RotationOffset:
|
|
|
|
+ case TransformationComp_RotationPivot:
|
|
|
|
+ case TransformationComp_ScalingOffset:
|
|
|
|
+ case TransformationComp_ScalingPivot:
|
|
|
|
+ case TransformationComp_Translation:
|
|
|
|
+ na = GenerateTranslationNodeAnim(chain_name,
|
|
|
|
+ target,
|
|
|
|
+ (*chain[i]).second,
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time);
|
|
|
|
+
|
|
|
|
+ // pivoting requires us to generate an inverse channel to undo the pivot translation
|
|
|
|
+ if (comp == TransformationComp_RotationPivot) {
|
|
|
|
+ const std::string& invName = NameTransformationChainNode(fixed_name, TransformationComp_RotationPivotInverse);
|
|
|
|
+ aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
|
|
|
+ target,
|
|
|
|
+ (*chain[i]).second,
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time,
|
|
|
|
+ true);
|
|
|
|
+
|
|
|
|
+ ai_assert(inv);
|
|
|
|
+ node_anims.push_back(inv);
|
|
|
|
+ }
|
|
|
|
+ else if (comp == TransformationComp_ScalingPivot) {
|
|
|
|
+ const std::string& invName = NameTransformationChainNode(fixed_name, TransformationComp_ScalingPivotInverse);
|
|
|
|
+ aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
|
|
|
+ target,
|
|
|
|
+ (*chain[i]).second,
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time,
|
|
|
|
+ true);
|
|
|
|
+
|
|
|
|
+ ai_assert(inv);
|
|
|
|
+ node_anims.push_back(inv);
|
|
}
|
|
}
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- catch(std::exception&) {
|
|
|
|
- std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
|
|
|
|
- throw;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- if(node_anims.size()) {
|
|
|
|
- anim->mChannels = new aiNodeAnim*[node_anims.size()]();
|
|
|
|
- anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
|
|
|
|
|
|
+ break;
|
|
|
|
|
|
- std::swap_ranges(node_anims.begin(),node_anims.end(),anim->mChannels);
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- // empty animations would fail validation, so drop them
|
|
|
|
- delete anim;
|
|
|
|
- animations.pop_back();
|
|
|
|
- FBXImporter::LogInfo("ignoring empty AnimationStack: " + name);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ case TransformationComp_Scaling:
|
|
|
|
+ na = GenerateScalingNodeAnim(chain_name,
|
|
|
|
+ target,
|
|
|
|
+ (*chain[i]).second,
|
|
|
|
+ layer_map,
|
|
|
|
+ max_time,
|
|
|
|
+ min_time
|
|
|
|
+ );
|
|
|
|
+ break;
|
|
|
|
|
|
- // for some mysterious reason, mDuration is simply the maximum key -- the
|
|
|
|
- // validator always assumes animations to start at zero.
|
|
|
|
- anim->mDuration = max_time /*- min_time */;
|
|
|
|
- anim->mTicksPerSecond = 1000.0;
|
|
|
|
|
|
+ default:
|
|
|
|
+ ai_assert(false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ai_assert(na);
|
|
|
|
+ node_anims.push_back(na);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|