diff options
author | Sukhitha Prabhath Jayathilake <pr.jayathilake@gmail.com> | 2011-05-26 21:07:38 +0400 |
---|---|---|
committer | Sukhitha Prabhath Jayathilake <pr.jayathilake@gmail.com> | 2011-05-26 21:07:38 +0400 |
commit | 93e992c5d980ce2186c025dbf6f4900b1ad78557 (patch) | |
tree | 0b8c3db1d69d71bfd9753790f6039f1bdeb280eb | |
parent | 36a720606e4689eaa2cc3c089a3ff6c27ed023ee (diff) |
AnimationExporter class refactoring.
Exported AnimationExporter class to a separate set of files.
-rw-r--r-- | source/blender/collada/AnimationExporter.cpp | 660 | ||||
-rw-r--r-- | source/blender/collada/AnimationExporter.h | 139 | ||||
-rw-r--r-- | source/blender/collada/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/collada/DocumentExporter.cpp | 635 |
4 files changed, 802 insertions, 634 deletions
diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp new file mode 100644 index 00000000000..8b2aada5e9a --- /dev/null +++ b/source/blender/collada/AnimationExporter.cpp @@ -0,0 +1,660 @@ +/* + * $Id: DocumentExporter.cpp 36898 2011-05-25 17:14:31Z phabtar $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "GeometryExporter.h" +#include "AnimationExporter.h" + +template<class Functor> +void forEachObjectInScene(Scene *sce, Functor &f) +{ + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + f(ob); + + base= base->next; + } +} + +void AnimationExporter::exportAnimations(Scene *sce) + { + if(hasAnimations(sce)) { + this->scene = sce; + + openLibrary(); + + forEachObjectInScene(sce, *this); + + closeLibrary(); + } + } + + // called for each exported object + void AnimationExporter::operator() (Object *ob) + { + if (!ob->adt || !ob->adt->action) return; + + FCurve *fcu = (FCurve*)ob->adt->action->curves.first; + + if (ob->type == OB_ARMATURE) { + if (!ob->data) return; + + bArmature *arm = (bArmature*)ob->data; + for (Bone *bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) + write_bone_animation(ob, bone); + } + else { + while (fcu) { + // TODO "rotation_quaternion" is also possible for objects (although euler is default) + if ((!strcmp(fcu->rna_path, "location") || !strcmp(fcu->rna_path, "scale")) || + (!strcmp(fcu->rna_path, "rotation_euler") && ob->rotmode == ROT_MODE_EUL)) + dae_animation(fcu, id_name(ob)); + + fcu = fcu->next; + } + } + } + + void AnimationExporter::dae_animation(FCurve *fcu, std::string ob_name) + { + const char *axis_names[] = {"X", "Y", "Z"}; + const char *axis_name = NULL; + char anim_id[200]; + + if (fcu->array_index < 3) + axis_name = axis_names[fcu->array_index]; + + BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char*)translate_id(ob_name).c_str(), + fcu->rna_path, axis_names[fcu->array_index]); + + // check rna_path is one of: rotation, scale, location + + openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); + + // create input source + std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name); + + // create output source + std::string output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name); + + // create interpolations source + std::string interpolation_id = create_interpolation_source(fcu->totvert, anim_id, axis_name); + + std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); + std::string empty; + sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); + sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); + + // this input is required + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); + + addSampler(sampler); + + std::string target = translate_id(ob_name) + + "/" + get_transform_sid(fcu->rna_path, -1, axis_name, true); + addChannel(COLLADABU::URI(empty, sampler_id), target); + + closeAnimation(); + } + + void AnimationExporter::write_bone_animation(Object *ob_arm, Bone *bone) + { + if (!ob_arm->adt) + return; + + //write bone animations for 3 transform types + //i=0 --> rotations + //i=1 --> scale + //i=2 --> location + for (int i = 0; i < 3; i++) + sample_and_write_bone_animation(ob_arm, bone, i); + + for (Bone *child = (Bone*)bone->childbase.first; child; child = child->next) + write_bone_animation(ob_arm, child); + } + + void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type) + { + bArmature *arm = (bArmature*)ob_arm->data; + int flag = arm->flag; + std::vector<float> fra; + char prefix[256]; + + BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone->name); + + bPoseChannel *pchan = get_pose_channel(ob_arm->pose, bone->name); + if (!pchan) + return; + + switch (transform_type) { + case 0: + find_rotation_frames(ob_arm, fra, prefix, pchan->rotmode); + break; + case 1: + find_frames(ob_arm, fra, prefix, "scale"); + break; + case 2: + find_frames(ob_arm, fra, prefix, "location"); + break; + default: + return; + } + + // exit rest position + if (flag & ARM_RESTPOS) { + arm->flag &= ~ARM_RESTPOS; + where_is_pose(scene, ob_arm); + } + + if (fra.size()) { + float *v = (float*)MEM_callocN(sizeof(float) * 3 * fra.size(), "temp. anim frames"); + sample_animation(v, fra, transform_type, bone, ob_arm); + + if (transform_type == 0) { + // write x, y, z curves separately if it is rotation + float *c = (float*)MEM_callocN(sizeof(float) * fra.size(), "temp. anim frames"); + for (int i = 0; i < 3; i++) { + for (unsigned int j = 0; j < fra.size(); j++) + c[j] = v[j * 3 + i]; + + dae_bone_animation(fra, c, transform_type, i, id_name(ob_arm), bone->name); + } + MEM_freeN(c); + } + else { + // write xyz at once if it is location or scale + dae_bone_animation(fra, v, transform_type, -1, id_name(ob_arm), bone->name); + } + + MEM_freeN(v); + } + + // restore restpos + if (flag & ARM_RESTPOS) + arm->flag = flag; + where_is_pose(scene, ob_arm); + } + + void AnimationExporter::sample_animation(float *v, std::vector<float> &frames, int type, Bone *bone, Object *ob_arm) + { + bPoseChannel *pchan, *parchan = NULL; + bPose *pose = ob_arm->pose; + + pchan = get_pose_channel(pose, bone->name); + + if (!pchan) + return; + + parchan = pchan->parent; + + enable_fcurves(ob_arm->adt->action, bone->name); + + std::vector<float>::iterator it; + for (it = frames.begin(); it != frames.end(); it++) { + float mat[4][4], ipar[4][4]; + + float ctime = bsystem_time(scene, ob_arm, *it, 0.0f); + + BKE_animsys_evaluate_animdata(&ob_arm->id, ob_arm->adt, *it, ADT_RECALC_ANIM); + where_is_pose_bone(scene, ob_arm, pchan, ctime, 1); + + // compute bone local mat + if (bone->parent) { + invert_m4_m4(ipar, parchan->pose_mat); + mul_m4_m4m4(mat, pchan->pose_mat, ipar); + } + else + copy_m4_m4(mat, pchan->pose_mat); + + switch (type) { + case 0: + mat4_to_eul(v, mat); + break; + case 1: + mat4_to_size(v, mat); + break; + case 2: + copy_v3_v3(v, mat[3]); + break; + } + + v += 3; + } + + enable_fcurves(ob_arm->adt->action, NULL); + } + + // dae_bone_animation -> add_bone_animation + // (blend this into dae_bone_animation) + void AnimationExporter::dae_bone_animation(std::vector<float> &fra, float *v, int tm_type, int axis, std::string ob_name, std::string bone_name) + { + const char *axis_names[] = {"X", "Y", "Z"}; + const char *axis_name = NULL; + char anim_id[200]; + bool is_rot = tm_type == 0; + + if (!fra.size()) + return; + + char rna_path[200]; + BLI_snprintf(rna_path, sizeof(rna_path), "pose.bones[\"%s\"].%s", bone_name.c_str(), + tm_type == 0 ? "rotation_quaternion" : (tm_type == 1 ? "scale" : "location")); + + if (axis > -1) + axis_name = axis_names[axis]; + + std::string transform_sid = get_transform_sid(NULL, tm_type, axis_name, false); + + BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char*)translate_id(ob_name).c_str(), + (char*)translate_id(bone_name).c_str(), (char*)transform_sid.c_str()); + + openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); + + // create input source + std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, fra, is_rot, anim_id, axis_name); + + // create output source + std::string output_id; + if (axis == -1) + output_id = create_xyz_source(v, fra.size(), anim_id); + else + output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, v, fra.size(), is_rot, anim_id, axis_name); + + // create interpolations source + std::string interpolation_id = create_interpolation_source(fra.size(), anim_id, axis_name); + + std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); + std::string empty; + sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); + sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); + + // TODO create in/out tangents source + + // this input is required + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); + + addSampler(sampler); + + std::string target = translate_id(ob_name + "_" + bone_name) + "/" + transform_sid; + addChannel(COLLADABU::URI(empty, sampler_id), target); + + closeAnimation(); + } + + float AnimationExporter::convert_time(float frame) + { + return FRA2TIME(frame); + } + + float AnimationExporter::convert_angle(float angle) + { + return COLLADABU::Math::Utils::radToDegF(angle); + } + + std::string AnimationExporter::get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) + { + switch(semantic) { + case COLLADASW::InputSemantic::INPUT: + return INPUT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::OUTPUT: + return OUTPUT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::INTERPOLATION: + return INTERPOLATION_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::IN_TANGENT: + return INTANGENT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::OUT_TANGENT: + return OUTTANGENT_SOURCE_ID_SUFFIX; + default: + break; + } + return ""; + } + + void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, + COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis) + { + switch(semantic) { + case COLLADASW::InputSemantic::INPUT: + param.push_back("TIME"); + break; + case COLLADASW::InputSemantic::OUTPUT: + if (is_rot) { + param.push_back("ANGLE"); + } + else { + if (axis) { + param.push_back(axis); + } + else { + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + } + } + break; + case COLLADASW::InputSemantic::IN_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: + param.push_back("X"); + param.push_back("Y"); + break; + default: + break; + } + } + + void AnimationExporter::get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool rotation, float *values, int *length) + { + switch (semantic) { + case COLLADASW::InputSemantic::INPUT: + *length = 1; + values[0] = convert_time(bezt->vec[1][0]); + break; + case COLLADASW::InputSemantic::OUTPUT: + *length = 1; + if (rotation) { + values[0] = convert_angle(bezt->vec[1][1]); + } + else { + values[0] = bezt->vec[1][1]; + } + break; + case COLLADASW::InputSemantic::IN_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: + // XXX + *length = 2; + break; + default: + *length = 0; + break; + } + } + + std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name) + { + std::string source_id = anim_id + get_semantic_suffix(semantic); + + //bool is_rotation = !strcmp(fcu->rna_path, "rotation"); + bool is_rotation = false; + + if (strstr(fcu->rna_path, "rotation")) is_rotation = true; + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(fcu->totvert); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, is_rotation, axis_name); + + source.prepareToAppendValues(); + + for (unsigned int i = 0; i < fcu->totvert; i++) { + float values[3]; // be careful! + int length = 0; + + get_source_values(&fcu->bezt[i], semantic, is_rotation, values, &length); + for (int j = 0; j < length; j++) + source.appendValues(values[j]); + } + + source.finish(); + + return source_id; + } + + std::string AnimationExporter::create_source_from_array(COLLADASW::InputSemantic::Semantics semantic, float *v, int tot, bool is_rot, const std::string& anim_id, const char *axis_name) + { + std::string source_id = anim_id + get_semantic_suffix(semantic); + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(tot); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, is_rot, axis_name); + + source.prepareToAppendValues(); + + for (int i = 0; i < tot; i++) { + float val = v[i]; + if (semantic == COLLADASW::InputSemantic::INPUT) + val = convert_time(val); + else if (is_rot) + val = convert_angle(val); + source.appendValues(val); + } + + source.finish(); + + return source_id; + } + + std::string AnimationExporter::create_source_from_vector(COLLADASW::InputSemantic::Semantics semantic, std::vector<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name) + { + std::string source_id = anim_id + get_semantic_suffix(semantic); + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(fra.size()); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, is_rot, axis_name); + + source.prepareToAppendValues(); + + std::vector<float>::iterator it; + for (it = fra.begin(); it != fra.end(); it++) { + float val = *it; + if (semantic == COLLADASW::InputSemantic::INPUT) + val = convert_time(val); + else if (is_rot) + val = convert_angle(val); + source.appendValues(val); + } + + source.finish(); + + return source_id; + } + + // only used for sources with OUTPUT semantic + std::string AnimationExporter::create_xyz_source(float *v, int tot, const std::string& anim_id) + { + COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; + std::string source_id = anim_id + get_semantic_suffix(semantic); + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(tot); + source.setAccessorStride(3); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, false, NULL); + + source.prepareToAppendValues(); + + for (int i = 0; i < tot; i++) { + source.appendValues(*v, *(v + 1), *(v + 2)); + v += 3; + } + + source.finish(); + + return source_id; + } + + std::string AnimationExporter::create_interpolation_source(int tot, const std::string& anim_id, const char *axis_name) + { + std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); + + COLLADASW::NameSource source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(tot); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("INTERPOLATION"); + + source.prepareToAppendValues(); + + for (int i = 0; i < tot; i++) { + source.appendValues(LINEAR_NAME); + } + + source.finish(); + + return source_id; + } + + // for rotation, axis name is always appended and the value of append_axis is ignored + std::string AnimationExporter::get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis) + { + std::string tm_name; + + // when given rna_path, determine tm_type from it + if (rna_path) { + char *name = extract_transform_name(rna_path); + + if (strstr(name, "rotation")) + tm_type = 0; + else if (!strcmp(name, "scale")) + tm_type = 1; + else if (!strcmp(name, "location")) + tm_type = 2; + else + tm_type = -1; + } + + switch (tm_type) { + case 0: + return std::string("rotation") + std::string(axis_name) + ".ANGLE"; + case 1: + tm_name = "scale"; + break; + case 2: + tm_name = "location"; + break; + default: + tm_name = ""; + break; + } + + if (tm_name.size()) { + if (append_axis) + return tm_name + std::string(".") + std::string(axis_name); + else + return tm_name; + } + + return std::string(""); + } + + char* AnimationExporter::extract_transform_name(char *rna_path) + { + char *dot = strrchr(rna_path, '.'); + return dot ? (dot + 1) : rna_path; + } + + void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name) + { + FCurve *fcu= (FCurve*)ob->adt->action->curves.first; + + for (; fcu; fcu = fcu->next) { + if (prefix && strncmp(prefix, fcu->rna_path, strlen(prefix))) + continue; + + char *name = extract_transform_name(fcu->rna_path); + if (!strcmp(name, tm_name)) { + for (unsigned int i = 0; i < fcu->totvert; i++) { + float f = fcu->bezt[i].vec[1][0]; + if (std::find(fra.begin(), fra.end(), f) == fra.end()) + fra.push_back(f); + } + } + } + + // keep the keys in ascending order + std::sort(fra.begin(), fra.end()); + } + + void AnimationExporter::find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode) + { + if (rotmode > 0) + find_frames(ob, fra, prefix, "rotation_euler"); + else if (rotmode == ROT_MODE_QUAT) + find_frames(ob, fra, prefix, "rotation_quaternion"); + /*else if (rotmode == ROT_MODE_AXISANGLE) + ;*/ + } + + // enable fcurves driving a specific bone, disable all the rest + // if bone_name = NULL enable all fcurves + void AnimationExporter::enable_fcurves(bAction *act, char *bone_name) + { + FCurve *fcu; + char prefix[200]; + + if (bone_name) + BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); + + for (fcu = (FCurve*)act->curves.first; fcu; fcu = fcu->next) { + if (bone_name) { + if (!strncmp(fcu->rna_path, prefix, strlen(prefix))) + fcu->flag &= ~FCURVE_DISABLED; + else + fcu->flag |= FCURVE_DISABLED; + } + else { + fcu->flag &= ~FCURVE_DISABLED; + } + } + } + + bool AnimationExporter::hasAnimations(Scene *sce) + { + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + FCurve *fcu = 0; + if(ob->adt && ob->adt->action) + fcu = (FCurve*)ob->adt->action->curves.first; + + if ((ob->type == OB_ARMATURE && ob->data) || fcu) { + return true; + } + base= base->next; + } + return false; + } diff --git a/source/blender/collada/AnimationExporter.h b/source/blender/collada/AnimationExporter.h new file mode 100644 index 00000000000..89db0788674 --- /dev/null +++ b/source/blender/collada/AnimationExporter.h @@ -0,0 +1,139 @@ +/* + * $Id: DocumentExporter.cpp 36898 2011-05-25 17:14:31Z phabtar $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +extern "C" +{ +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_armature_types.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_fcurve.h" +#include "BKE_animsys.h" +#ifdef NAN_BUILDINFO +extern char build_rev[]; +#endif +} + +#include "MEM_guardedalloc.h" + +#include "BKE_action.h" // pose functions +#include "BKE_armature.h" +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_listbase.h" + +#include "RNA_access.h" + +#include "COLLADASWSource.h" +#include "COLLADASWInstanceGeometry.h" +#include "COLLADASWInputList.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWVertices.h" +#include "COLLADASWLibraryAnimations.h" +#include "COLLADASWParamTemplate.h" +#include "COLLADASWParamBase.h" +#include "COLLADASWSampler.h" +#include "COLLADASWConstants.h" +#include "COLLADASWBaseInputElement.h" + +#include "collada_internal.h" + +#include <vector> +#include <algorithm> // std::find + +class AnimationExporter: COLLADASW::LibraryAnimations +{ +private: + Scene *scene; + COLLADASW::StreamWriter *sw; + +public: + + AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) { this->sw = sw; } + + + void exportAnimations(Scene *sce); + + // called for each exported object + void operator() (Object *ob); + +protected: + + void dae_animation(FCurve *fcu, std::string ob_name); + + void write_bone_animation(Object *ob_arm, Bone *bone); + + void sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type); + + void sample_animation(float *v, std::vector<float> &frames, int type, Bone *bone, Object *ob_arm); + + // dae_bone_animation -> add_bone_animation + // (blend this into dae_bone_animation) + void dae_bone_animation(std::vector<float> &fra, float *v, int tm_type, int axis, std::string ob_name, std::string bone_name); + + float convert_time(float frame); + + float convert_angle(float angle); + + std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic); + + void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, + COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis); + + void get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool rotation, float *values, int *length); + + std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name); + + std::string create_source_from_array(COLLADASW::InputSemantic::Semantics semantic, float *v, int tot, bool is_rot, const std::string& anim_id, const char *axis_name); + + std::string create_source_from_vector(COLLADASW::InputSemantic::Semantics semantic, std::vector<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name); + + std::string create_xyz_source(float *v, int tot, const std::string& anim_id); + + std::string create_interpolation_source(int tot, const std::string& anim_id, const char *axis_name); + + // for rotation, axis name is always appended and the value of append_axis is ignored + std::string get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis); + + void find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name); + + void find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode); + + // enable fcurves driving a specific bone, disable all the rest + // if bone_name = NULL enable all fcurves + void enable_fcurves(bAction *act, char *bone_name); + + bool hasAnimations(Scene *sce); + + char* extract_transform_name(char *rna_path); +};
\ No newline at end of file diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt index fa7bfee8ee5..130e18f4c9a 100644 --- a/source/blender/collada/CMakeLists.txt +++ b/source/blender/collada/CMakeLists.txt @@ -58,6 +58,7 @@ endif() set(SRC AnimationImporter.cpp + AnimationExporter.cpp ArmatureExporter.cpp ArmatureImporter.cpp CameraExporter.cpp @@ -80,6 +81,7 @@ set(SRC collada_utils.cpp AnimationImporter.h + AnimationExporter.h ArmatureExporter.h ArmatureImporter.h CameraExporter.h diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp index bb0bc5e1e76..0bdf41f15eb 100644 --- a/source/blender/collada/DocumentExporter.cpp +++ b/source/blender/collada/DocumentExporter.cpp @@ -114,6 +114,7 @@ extern char build_rev[]; #include "TransformWriter.h" #include "ArmatureExporter.h" +#include "AnimationExporter.h" #include "CameraExporter.h" #include "EffectExporter.h" #include "GeometryExporter.h" @@ -298,640 +299,6 @@ public: // TODO: it would be better to instantiate animations rather than create a new one per object // COLLADA allows this through multiple <channel>s in <animation>. // For this to work, we need to know objects that use a certain action. -class AnimationExporter: COLLADASW::LibraryAnimations -{ - Scene *scene; - COLLADASW::StreamWriter *sw; - -public: - - AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) { this->sw = sw; } - - - - void exportAnimations(Scene *sce) - { - if(hasAnimations(sce)) { - this->scene = sce; - - openLibrary(); - - forEachObjectInScene(sce, *this); - - closeLibrary(); - } - } - - // called for each exported object - void operator() (Object *ob) - { - if (!ob->adt || !ob->adt->action) return; - - FCurve *fcu = (FCurve*)ob->adt->action->curves.first; - - if (ob->type == OB_ARMATURE) { - if (!ob->data) return; - - bArmature *arm = (bArmature*)ob->data; - for (Bone *bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) - write_bone_animation(ob, bone); - } - else { - while (fcu) { - // TODO "rotation_quaternion" is also possible for objects (although euler is default) - if ((!strcmp(fcu->rna_path, "location") || !strcmp(fcu->rna_path, "scale")) || - (!strcmp(fcu->rna_path, "rotation_euler") && ob->rotmode == ROT_MODE_EUL)) - dae_animation(fcu, id_name(ob)); - - fcu = fcu->next; - } - } - } - -protected: - - void dae_animation(FCurve *fcu, std::string ob_name) - { - const char *axis_names[] = {"X", "Y", "Z"}; - const char *axis_name = NULL; - char anim_id[200]; - - if (fcu->array_index < 3) - axis_name = axis_names[fcu->array_index]; - - BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char*)translate_id(ob_name).c_str(), - fcu->rna_path, axis_names[fcu->array_index]); - - // check rna_path is one of: rotation, scale, location - - openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); - - // create input source - std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name); - - // create output source - std::string output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name); - - // create interpolations source - std::string interpolation_id = create_interpolation_source(fcu->totvert, anim_id, axis_name); - - std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; - COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); - std::string empty; - sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); - sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); - - // this input is required - sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); - - addSampler(sampler); - - std::string target = translate_id(ob_name) - + "/" + get_transform_sid(fcu->rna_path, -1, axis_name, true); - addChannel(COLLADABU::URI(empty, sampler_id), target); - - closeAnimation(); - } - - void write_bone_animation(Object *ob_arm, Bone *bone) - { - if (!ob_arm->adt) - return; - - //write bone animations for 3 transform types - //i=0 --> rotations - //i=1 --> scale - //i=2 --> location - for (int i = 0; i < 3; i++) - sample_and_write_bone_animation(ob_arm, bone, i); - - for (Bone *child = (Bone*)bone->childbase.first; child; child = child->next) - write_bone_animation(ob_arm, child); - } - - void sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type) - { - bArmature *arm = (bArmature*)ob_arm->data; - int flag = arm->flag; - std::vector<float> fra; - char prefix[256]; - - BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone->name); - - bPoseChannel *pchan = get_pose_channel(ob_arm->pose, bone->name); - if (!pchan) - return; - - switch (transform_type) { - case 0: - find_rotation_frames(ob_arm, fra, prefix, pchan->rotmode); - break; - case 1: - find_frames(ob_arm, fra, prefix, "scale"); - break; - case 2: - find_frames(ob_arm, fra, prefix, "location"); - break; - default: - return; - } - - // exit rest position - if (flag & ARM_RESTPOS) { - arm->flag &= ~ARM_RESTPOS; - where_is_pose(scene, ob_arm); - } - - if (fra.size()) { - float *v = (float*)MEM_callocN(sizeof(float) * 3 * fra.size(), "temp. anim frames"); - sample_animation(v, fra, transform_type, bone, ob_arm); - - if (transform_type == 0) { - // write x, y, z curves separately if it is rotation - float *c = (float*)MEM_callocN(sizeof(float) * fra.size(), "temp. anim frames"); - for (int i = 0; i < 3; i++) { - for (unsigned int j = 0; j < fra.size(); j++) - c[j] = v[j * 3 + i]; - - dae_bone_animation(fra, c, transform_type, i, id_name(ob_arm), bone->name); - } - MEM_freeN(c); - } - else { - // write xyz at once if it is location or scale - dae_bone_animation(fra, v, transform_type, -1, id_name(ob_arm), bone->name); - } - - MEM_freeN(v); - } - - // restore restpos - if (flag & ARM_RESTPOS) - arm->flag = flag; - where_is_pose(scene, ob_arm); - } - - void sample_animation(float *v, std::vector<float> &frames, int type, Bone *bone, Object *ob_arm) - { - bPoseChannel *pchan, *parchan = NULL; - bPose *pose = ob_arm->pose; - - pchan = get_pose_channel(pose, bone->name); - - if (!pchan) - return; - - parchan = pchan->parent; - - enable_fcurves(ob_arm->adt->action, bone->name); - - std::vector<float>::iterator it; - for (it = frames.begin(); it != frames.end(); it++) { - float mat[4][4], ipar[4][4]; - - float ctime = bsystem_time(scene, ob_arm, *it, 0.0f); - - BKE_animsys_evaluate_animdata(&ob_arm->id, ob_arm->adt, *it, ADT_RECALC_ANIM); - where_is_pose_bone(scene, ob_arm, pchan, ctime, 1); - - // compute bone local mat - if (bone->parent) { - invert_m4_m4(ipar, parchan->pose_mat); - mul_m4_m4m4(mat, pchan->pose_mat, ipar); - } - else - copy_m4_m4(mat, pchan->pose_mat); - - switch (type) { - case 0: - mat4_to_eul(v, mat); - break; - case 1: - mat4_to_size(v, mat); - break; - case 2: - copy_v3_v3(v, mat[3]); - break; - } - - v += 3; - } - - enable_fcurves(ob_arm->adt->action, NULL); - } - - // dae_bone_animation -> add_bone_animation - // (blend this into dae_bone_animation) - void dae_bone_animation(std::vector<float> &fra, float *v, int tm_type, int axis, std::string ob_name, std::string bone_name) - { - const char *axis_names[] = {"X", "Y", "Z"}; - const char *axis_name = NULL; - char anim_id[200]; - bool is_rot = tm_type == 0; - - if (!fra.size()) - return; - - char rna_path[200]; - BLI_snprintf(rna_path, sizeof(rna_path), "pose.bones[\"%s\"].%s", bone_name.c_str(), - tm_type == 0 ? "rotation_quaternion" : (tm_type == 1 ? "scale" : "location")); - - if (axis > -1) - axis_name = axis_names[axis]; - - std::string transform_sid = get_transform_sid(NULL, tm_type, axis_name, false); - - BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char*)translate_id(ob_name).c_str(), - (char*)translate_id(bone_name).c_str(), (char*)transform_sid.c_str()); - - openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); - - // create input source - std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, fra, is_rot, anim_id, axis_name); - - // create output source - std::string output_id; - if (axis == -1) - output_id = create_xyz_source(v, fra.size(), anim_id); - else - output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, v, fra.size(), is_rot, anim_id, axis_name); - - // create interpolations source - std::string interpolation_id = create_interpolation_source(fra.size(), anim_id, axis_name); - - std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; - COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); - std::string empty; - sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); - sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); - - // TODO create in/out tangents source - - // this input is required - sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); - - addSampler(sampler); - - std::string target = translate_id(ob_name + "_" + bone_name) + "/" + transform_sid; - addChannel(COLLADABU::URI(empty, sampler_id), target); - - closeAnimation(); - } - - float convert_time(float frame) - { - return FRA2TIME(frame); - } - - float convert_angle(float angle) - { - return COLLADABU::Math::Utils::radToDegF(angle); - } - - std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) - { - switch(semantic) { - case COLLADASW::InputSemantic::INPUT: - return INPUT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::OUTPUT: - return OUTPUT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::INTERPOLATION: - return INTERPOLATION_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::IN_TANGENT: - return INTANGENT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::OUT_TANGENT: - return OUTTANGENT_SOURCE_ID_SUFFIX; - default: - break; - } - return ""; - } - - void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, - COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis) - { - switch(semantic) { - case COLLADASW::InputSemantic::INPUT: - param.push_back("TIME"); - break; - case COLLADASW::InputSemantic::OUTPUT: - if (is_rot) { - param.push_back("ANGLE"); - } - else { - if (axis) { - param.push_back(axis); - } - else { - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - } - } - break; - case COLLADASW::InputSemantic::IN_TANGENT: - case COLLADASW::InputSemantic::OUT_TANGENT: - param.push_back("X"); - param.push_back("Y"); - break; - default: - break; - } - } - - void get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool rotation, float *values, int *length) - { - switch (semantic) { - case COLLADASW::InputSemantic::INPUT: - *length = 1; - values[0] = convert_time(bezt->vec[1][0]); - break; - case COLLADASW::InputSemantic::OUTPUT: - *length = 1; - if (rotation) { - values[0] = convert_angle(bezt->vec[1][1]); - } - else { - values[0] = bezt->vec[1][1]; - } - break; - case COLLADASW::InputSemantic::IN_TANGENT: - case COLLADASW::InputSemantic::OUT_TANGENT: - // XXX - *length = 2; - break; - default: - *length = 0; - break; - } - } - - std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name) - { - std::string source_id = anim_id + get_semantic_suffix(semantic); - - //bool is_rotation = !strcmp(fcu->rna_path, "rotation"); - bool is_rotation = false; - - if (strstr(fcu->rna_path, "rotation")) is_rotation = true; - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(fcu->totvert); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, is_rotation, axis_name); - - source.prepareToAppendValues(); - - for (unsigned int i = 0; i < fcu->totvert; i++) { - float values[3]; // be careful! - int length = 0; - - get_source_values(&fcu->bezt[i], semantic, is_rotation, values, &length); - for (int j = 0; j < length; j++) - source.appendValues(values[j]); - } - - source.finish(); - - return source_id; - } - - std::string create_source_from_array(COLLADASW::InputSemantic::Semantics semantic, float *v, int tot, bool is_rot, const std::string& anim_id, const char *axis_name) - { - std::string source_id = anim_id + get_semantic_suffix(semantic); - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(tot); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, is_rot, axis_name); - - source.prepareToAppendValues(); - - for (int i = 0; i < tot; i++) { - float val = v[i]; - if (semantic == COLLADASW::InputSemantic::INPUT) - val = convert_time(val); - else if (is_rot) - val = convert_angle(val); - source.appendValues(val); - } - - source.finish(); - - return source_id; - } - - std::string create_source_from_vector(COLLADASW::InputSemantic::Semantics semantic, std::vector<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name) - { - std::string source_id = anim_id + get_semantic_suffix(semantic); - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(fra.size()); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, is_rot, axis_name); - - source.prepareToAppendValues(); - - std::vector<float>::iterator it; - for (it = fra.begin(); it != fra.end(); it++) { - float val = *it; - if (semantic == COLLADASW::InputSemantic::INPUT) - val = convert_time(val); - else if (is_rot) - val = convert_angle(val); - source.appendValues(val); - } - - source.finish(); - - return source_id; - } - - // only used for sources with OUTPUT semantic - std::string create_xyz_source(float *v, int tot, const std::string& anim_id) - { - COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; - std::string source_id = anim_id + get_semantic_suffix(semantic); - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(tot); - source.setAccessorStride(3); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, false, NULL); - - source.prepareToAppendValues(); - - for (int i = 0; i < tot; i++) { - source.appendValues(*v, *(v + 1), *(v + 2)); - v += 3; - } - - source.finish(); - - return source_id; - } - - std::string create_interpolation_source(int tot, const std::string& anim_id, const char *axis_name) - { - std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); - - COLLADASW::NameSource source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(tot); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("INTERPOLATION"); - - source.prepareToAppendValues(); - - for (int i = 0; i < tot; i++) { - source.appendValues(LINEAR_NAME); - } - - source.finish(); - - return source_id; - } - - // for rotation, axis name is always appended and the value of append_axis is ignored - std::string get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis) - { - std::string tm_name; - - // when given rna_path, determine tm_type from it - if (rna_path) { - char *name = extract_transform_name(rna_path); - - if (strstr(name, "rotation")) - tm_type = 0; - else if (!strcmp(name, "scale")) - tm_type = 1; - else if (!strcmp(name, "location")) - tm_type = 2; - else - tm_type = -1; - } - - switch (tm_type) { - case 0: - return std::string("rotation") + std::string(axis_name) + ".ANGLE"; - case 1: - tm_name = "scale"; - break; - case 2: - tm_name = "location"; - break; - default: - tm_name = ""; - break; - } - - if (tm_name.size()) { - if (append_axis) - return tm_name + std::string(".") + std::string(axis_name); - else - return tm_name; - } - - return std::string(""); - } - - char *extract_transform_name(char *rna_path) - { - char *dot = strrchr(rna_path, '.'); - return dot ? (dot + 1) : rna_path; - } - - void find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name) - { - FCurve *fcu= (FCurve*)ob->adt->action->curves.first; - - for (; fcu; fcu = fcu->next) { - if (prefix && strncmp(prefix, fcu->rna_path, strlen(prefix))) - continue; - - char *name = extract_transform_name(fcu->rna_path); - if (!strcmp(name, tm_name)) { - for (unsigned int i = 0; i < fcu->totvert; i++) { - float f = fcu->bezt[i].vec[1][0]; - if (std::find(fra.begin(), fra.end(), f) == fra.end()) - fra.push_back(f); - } - } - } - - // keep the keys in ascending order - std::sort(fra.begin(), fra.end()); - } - - void find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode) - { - if (rotmode > 0) - find_frames(ob, fra, prefix, "rotation_euler"); - else if (rotmode == ROT_MODE_QUAT) - find_frames(ob, fra, prefix, "rotation_quaternion"); - /*else if (rotmode == ROT_MODE_AXISANGLE) - ;*/ - } - - // enable fcurves driving a specific bone, disable all the rest - // if bone_name = NULL enable all fcurves - void enable_fcurves(bAction *act, char *bone_name) - { - FCurve *fcu; - char prefix[200]; - - if (bone_name) - BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); - - for (fcu = (FCurve*)act->curves.first; fcu; fcu = fcu->next) { - if (bone_name) { - if (!strncmp(fcu->rna_path, prefix, strlen(prefix))) - fcu->flag &= ~FCURVE_DISABLED; - else - fcu->flag |= FCURVE_DISABLED; - } - else { - fcu->flag &= ~FCURVE_DISABLED; - } - } - } - - bool hasAnimations(Scene *sce) - { - Base *base= (Base*) sce->base.first; - while(base) { - Object *ob = base->object; - - FCurve *fcu = 0; - if(ob->adt && ob->adt->action) - fcu = (FCurve*)ob->adt->action->curves.first; - - if ((ob->type == OB_ARMATURE && ob->data) || fcu) { - return true; - } - base= base->next; - } - return false; - } -}; void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename) { |