COLLADA branch: bone anim export works. Export-import roundtrip is possible.

Importer now takes mesh bind position into account.

TODO: read/write object-level anim on armatures and fix memleaks.
This commit is contained in:
Arystanbek Dyussenov 2010-01-05 16:07:10 +00:00
parent 18eb6d9827
commit 06d548bd6f
2 changed files with 455 additions and 266 deletions

@ -21,6 +21,7 @@ extern "C"
{ {
#include "BKE_DerivedMesh.h" #include "BKE_DerivedMesh.h"
#include "BKE_fcurve.h" #include "BKE_fcurve.h"
#include "BKE_animsys.h"
#include "BLI_util.h" #include "BLI_util.h"
#include "BLI_fileops.h" #include "BLI_fileops.h"
#include "ED_keyframing.h" #include "ED_keyframing.h"
@ -36,6 +37,7 @@ extern "C"
#include "BKE_armature.h" #include "BKE_armature.h"
#include "BKE_image.h" #include "BKE_image.h"
#include "BKE_utildefines.h" #include "BKE_utildefines.h"
#include "BKE_object.h"
#include "BLI_math.h" #include "BLI_math.h"
#include "BLI_string.h" #include "BLI_string.h"
@ -198,6 +200,12 @@ static std::string get_camera_id(Object *ob)
return translate_id(id_name(ob)) + "-camera"; return translate_id(id_name(ob)) + "-camera";
} }
std::string get_joint_id(Bone *bone, Object *ob_arm)
{
return translate_id(id_name(ob_arm) + "_" + bone->name);
}
/* /*
Utilities to avoid code duplication. Utilities to avoid code duplication.
Definition can take some time to understand, but they should be useful. Definition can take some time to understand, but they should be useful.
@ -851,11 +859,6 @@ private:
return ob_arm; return ob_arm;
} }
std::string get_joint_id(Bone *bone, Object *ob_arm)
{
return translate_id(id_name(ob_arm) + "_" + bone->name);
}
std::string get_joint_sid(Bone *bone) std::string get_joint_sid(Bone *bone)
{ {
char name[100]; char name[100];
@ -1734,7 +1737,9 @@ public:
class AnimationExporter: COLLADASW::LibraryAnimations class AnimationExporter: COLLADASW::LibraryAnimations
{ {
Scene *scene; Scene *scene;
public: public:
AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) {} AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) {}
void exportAnimations(Scene *sce) void exportAnimations(Scene *sce)
@ -1748,17 +1753,278 @@ public:
closeLibrary(); closeLibrary();
} }
// create <animation> for each transform axis // called for each exported object
void operator() (Object *ob)
{
if (!ob->adt || !ob->adt->action) return;
float convert_time(float frame) { 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];
char anim_name[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]);
BLI_snprintf(anim_name, sizeof(anim_name), "%s.%s.%s",
(char*)ob_name.c_str(), fcu->rna_path, axis_names[fcu->array_index]);
// check rna_path is one of: rotation, scale, location
openAnimation(anim_id, anim_name);
// create input source
std::string input_id = create_source_from_fcurve(Sampler::INPUT, fcu, anim_id, axis_name);
// create output source
std::string output_id = create_source_from_fcurve(Sampler::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(sampler_id);
std::string empty;
sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
// this input is required
sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
addSampler(sampler);
std::string target = translate_id(ob_name)
+ "/" + get_transform_sid(fcu->rna_path, -1, axis_name);
addChannel(COLLADABU::URI(empty, sampler_id), target);
closeAnimation();
}
void write_bone_animation(Object *ob_arm, Bone *bone)
{
if (!ob_arm->adt)
return;
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 (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);
// 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];
char anim_name[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);
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());
BLI_snprintf(anim_name, sizeof(anim_name), "%s.%s.%s",
(char*)ob_name.c_str(), (char*)bone_name.c_str(), (char*)transform_sid.c_str());
// TODO check rna_path is one of: rotation, scale, location
openAnimation(anim_id, anim_name);
// create input source
std::string input_id = create_source_from_vector(Sampler::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(Sampler::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(sampler_id);
std::string empty;
sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
// TODO create in/out tangents source
// this input is required
sampler.addInput(Sampler::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); return FRA2TIME(frame);
} }
float convert_angle(float angle) { float convert_angle(float angle)
{
return COLLADABU::Math::Utils::radToDegF(angle); return COLLADABU::Math::Utils::radToDegF(angle);
} }
std::string get_semantic_suffix(Sampler::Semantic semantic) { std::string get_semantic_suffix(Sampler::Semantic semantic)
{
switch(semantic) { switch(semantic) {
case Sampler::INPUT: case Sampler::INPUT:
return INPUT_SOURCE_ID_SUFFIX; return INPUT_SOURCE_ID_SUFFIX;
@ -1775,18 +2041,26 @@ public:
} }
void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
Sampler::Semantic semantic, bool rotation, const char *axis) { Sampler::Semantic semantic, bool is_rot, const char *axis)
{
switch(semantic) { switch(semantic) {
case Sampler::INPUT: case Sampler::INPUT:
param.push_back("TIME"); param.push_back("TIME");
break; break;
case Sampler::OUTPUT: case Sampler::OUTPUT:
if (rotation) { if (is_rot) {
param.push_back("ANGLE"); param.push_back("ANGLE");
} }
else { else {
if (axis) {
param.push_back(axis); param.push_back(axis);
} }
else {
param.push_back("X");
param.push_back("Y");
param.push_back("Z");
}
}
break; break;
case Sampler::IN_TANGENT: case Sampler::IN_TANGENT:
case Sampler::OUT_TANGENT: case Sampler::OUT_TANGENT:
@ -1820,7 +2094,7 @@ public:
} }
} }
std::string create_source(Sampler::Semantic semantic, FCurve *fcu, std::string& anim_id, const char *axis_name) std::string create_source_from_fcurve(Sampler::Semantic semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name)
{ {
std::string source_id = anim_id + get_semantic_suffix(semantic); std::string source_id = anim_id + get_semantic_suffix(semantic);
@ -1854,16 +2128,100 @@ public:
return source_id; return source_id;
} }
std::string create_interpolation_source(FCurve *fcu, std::string& anim_id, const char *axis_name) std::string create_source_from_array(Sampler::Semantic 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 &param = 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 == Sampler::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(Sampler::Semantic 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 &param = 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 == Sampler::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)
{
Sampler::Semantic semantic = Sampler::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 &param = 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(Sampler::INTERPOLATION); std::string source_id = anim_id + get_semantic_suffix(Sampler::INTERPOLATION);
//bool is_rotation = !strcmp(fcu->rna_path, "rotation");
COLLADASW::NameSource source(mSW); COLLADASW::NameSource source(mSW);
source.setId(source_id); source.setId(source_id);
source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(fcu->totvert); source.setAccessorCount(tot);
source.setAccessorStride(1); source.setAccessorStride(1);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList(); COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@ -1871,8 +2229,7 @@ public:
source.prepareToAppendValues(); source.prepareToAppendValues();
for (int i = 0; i < fcu->totvert; i++) { for (int i = 0; i < tot; i++) {
// XXX
source.appendValues(LINEAR_NAME); source.appendValues(LINEAR_NAME);
} }
@ -1881,264 +2238,80 @@ public:
return source_id; return source_id;
} }
std::string get_transform_sid(char *rna_path, const char *axis_name) std::string get_transform_sid(char *rna_path, int tm_type, const char *axis_name)
{ {
// if (!strcmp(rna_path, "rotation")) if (rna_path) {
// return std::string(rna_path) + axis_name;
// return std::string(rna_path) + "." + axis_name;
char *name = extract_transform_name(rna_path); char *name = extract_transform_name(rna_path);
if (strstr(name, "rotation")) if (strstr(name, "rotation"))
return std::string("rotation") + axis_name; return std::string("rotation") + axis_name;
else if (!strcmp(name, "location") || !strcmp(name, "scale")) else if (!strcmp(name, "location") || !strcmp(name, "scale"))
return std::string(name) + "." + axis_name; return std::string(name);
}
else {
if (tm_type == 0)
return std::string("rotation") + axis_name;
else
return tm_type == 1 ? "scale" : "location";
}
return NULL; return NULL;
} }
void add_animation(FCurve *fcu, std::string ob_name)
{
const char *axis_names[] = {"X", "Y", "Z"};
const char *axis_name = NULL;
char c_anim_id[100]; // careful!
char c_anim_name[100]; // careful!
if (fcu->array_index < 3)
axis_name = axis_names[fcu->array_index];
BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)translate_id(ob_name).c_str(),
fcu->rna_path, axis_names[fcu->array_index]);
std::string anim_id(c_anim_id);
BLI_snprintf(c_anim_name, sizeof(c_anim_name), "%s.%s.%s",
(char*)ob_name.c_str(), fcu->rna_path, axis_names[fcu->array_index]);
std::string anim_name = c_anim_name;
// check rna_path is one of: rotation, scale, location
openAnimation(anim_id, anim_name);
// create input source
std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
// create output source
std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
// create interpolations source
std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
std::string empty;
sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
// this input is required
sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
addSampler(sampler);
std::string target = translate_id(ob_name)
+ "/" + get_transform_sid(fcu->rna_path, axis_name);
addChannel(COLLADABU::URI(empty, sampler_id), target);
closeAnimation();
}
void add_bone_animation(FCurve *fcu, std::string ob_name, std::string bone_name)
{
const char *axis_names[] = {"X", "Y", "Z"};
const char *axis_name = NULL;
char c_anim_id[100]; // careful!
char c_anim_name[100]; // careful!
if (fcu->array_index < 3)
axis_name = axis_names[fcu->array_index];
std::string transform_sid = get_transform_sid(fcu->rna_path, axis_name);
BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)translate_id(ob_name).c_str(),
(char*)translate_id(bone_name).c_str(), (char*)transform_sid.c_str());
std::string anim_id(c_anim_id);
BLI_snprintf(c_anim_name, sizeof(c_anim_name), "%s.%s.%s",
(char*)ob_name.c_str(), (char*)bone_name.c_str(), (char*)transform_sid.c_str());
std::string anim_name(c_anim_name);
// check rna_path is one of: rotation, scale, location
openAnimation(anim_id, anim_name);
// create input source
std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
// create output source
std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
// create interpolations source
std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
std::string empty;
sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
// this input is required
sampler.addInput(Sampler::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();
}
FCurve *create_fcurve(int array_index, char *rna_path)
{
FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
fcu->array_index = array_index;
return fcu;
}
void create_bezt(FCurve *fcu, float frame, float output)
{
BezTriple bez;
memset(&bez, 0, sizeof(BezTriple));
bez.vec[1][0] = frame;
bez.vec[1][1] = output;
bez.ipo = U.ipo_new; /* use default interpolation mode here... */
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez, 0);
calchandles_fcurve(fcu);
}
void change_quat_to_eul(Object *ob, std::vector<FCurve*> rcurves, char *grpname)
{
FCurve *quatcu[4] = {NULL, NULL, NULL, NULL};
int i;
for (i = 0; i < rcurves.size(); i++)
quatcu[rcurves[i]->array_index] = rcurves[i];
char *rna_path = rcurves[0]->rna_path;
FCurve *eulcu[3] = {
create_fcurve(0, rna_path),
create_fcurve(1, rna_path),
create_fcurve(2, rna_path)
};
for (i = 0; i < 4; i++) {
FCurve *cu = quatcu[i];
if (!cu) continue;
for (int j = 0; j < cu->totvert; j++) {
float frame = cu->bezt[j].vec[1][0];
float quat[4] = {
quatcu[0] ? evaluate_fcurve(quatcu[0], frame) : 1.0f,
quatcu[1] ? evaluate_fcurve(quatcu[1], frame) : 0.0f,
quatcu[2] ? evaluate_fcurve(quatcu[2], frame) : 0.0f,
quatcu[3] ? evaluate_fcurve(quatcu[3], frame) : 0.0f
};
float eul[3];
quat_to_eul( eul,quat);
for (int k = 0; k < 3; k++)
create_bezt(eulcu[k], frame, eul[k]);
}
}
for (i = 0; i < 3; i++) {
add_bone_animation(eulcu[i], id_name(ob), std::string(grpname));
free_fcurve(eulcu[i]);
}
}
char *extract_transform_name(char *rna_path) char *extract_transform_name(char *rna_path)
{ {
char *dot = strrchr(rna_path, '.'); char *dot = strrchr(rna_path, '.');
return dot ? (dot + 1) : rna_path; return dot ? (dot + 1) : rna_path;
} }
// called for each exported object void find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name)
void operator() (Object *ob)
{ {
if (!ob->adt || !ob->adt->action) return;
FCurve *fcu= (FCurve*)ob->adt->action->curves.first; FCurve *fcu= (FCurve*)ob->adt->action->curves.first;
if (ob->type == OB_ARMATURE) { for (; fcu; fcu = fcu->next) {
std::map< bActionGroup*, std::vector<FCurve*> > lcurve_map, quatcurve_map, eulcurve_map; if (prefix && strncmp(prefix, fcu->rna_path, strlen(prefix)))
continue;
while (fcu) {
// rna path should start with "pose.bones"
if (strstr(fcu->rna_path, "pose.bones") == fcu->rna_path) {
char *name = extract_transform_name(fcu->rna_path); char *name = extract_transform_name(fcu->rna_path);
if (!strcmp(name, tm_name)) {
for (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);
}
}
}
}
if (!strcmp(name, "rotation_quaternion")) void find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode)
quatcurve_map[fcu->grp].push_back(fcu); {
else if (!strcmp(name, "rotation_euler")) if (rotmode > 0)
eulcurve_map[fcu->grp].push_back(fcu); find_frames(ob, fra, prefix, "rotation_euler");
else if (!strcmp(name, "scale") || !strcmp(name, "location")) else if (rotmode == ROT_MODE_QUAT)
lcurve_map[fcu->grp].push_back(fcu); 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 else
fprintf(stderr, "warning: not writing fcurve for %s\n", name); fcu->flag |= FCURVE_DISABLED;
}
fcu = fcu->next;
}
for (bPoseChannel *pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) {
int i;
char *grpname = pchan->name;
bActionGroup *grp = action_groups_find_named(ob->adt->action, grpname);
if (!grp) continue;
// write animation for location and scale
if (lcurve_map.find(grp) != lcurve_map.end()) {
std::vector<FCurve*> &lcurves = lcurve_map[grp];
for (i = 0; i < lcurves.size(); i++)
add_bone_animation(lcurves[i], id_name(ob), std::string(grpname));
}
// rotation
// FIXME, this only supports XYZ order now, need to support others too
if (pchan->rotmode == ROT_MODE_EUL) {
if (eulcurve_map.find(grp) != eulcurve_map.end()) {
std::vector<FCurve*> &eulcu = eulcurve_map[grp];
// write euler values "as is"
for (i = 0; i < eulcu.size(); i++)
add_bone_animation(eulcu[i], id_name(ob), std::string(grpname));
}
}
else if (pchan->rotmode == ROT_MODE_QUAT) {
// convert rotation to euler and write animation
if (quatcurve_map.find(grp) != quatcurve_map.end())
change_quat_to_eul(ob, quatcurve_map[grp], grpname);
}
}
} }
else { else {
while (fcu) { fcu->flag &= ~FCURVE_DISABLED;
// 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))
add_animation(fcu, id_name(ob));
fcu = fcu->next;
} }
} }
} }
@ -2210,3 +2383,12 @@ void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename)
void DocumentExporter::exportScenes(const char* filename) void DocumentExporter::exportScenes(const char* filename)
{ {
} }
/*
NOTES:
* AnimationExporter::sample_animation enables all curves on armature, this is undesirable for a user
*/

@ -43,6 +43,8 @@ extern "C"
#include "ED_armature.h" #include "ED_armature.h"
#include "ED_mesh.h" // ED_vgroup_vert_add, ... #include "ED_mesh.h" // ED_vgroup_vert_add, ...
#include "ED_anim_api.h" #include "ED_anim_api.h"
#include "ED_object.h"
#include "WM_types.h" #include "WM_types.h"
#include "WM_api.h" #include "WM_api.h"
@ -82,6 +84,7 @@ extern "C"
#include "DNA_mesh_types.h" #include "DNA_mesh_types.h"
#include "DNA_material_types.h" #include "DNA_material_types.h"
#include "DNA_scene_types.h" #include "DNA_scene_types.h"
#include "DNA_modifier_types.h"
#include "MEM_guardedalloc.h" #include "MEM_guardedalloc.h"
@ -497,20 +500,24 @@ private:
void link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, void link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid,
TransformReader *tm) TransformReader *tm)
{ {
Object workob;
Scene *scene = CTX_data_scene(C);
ModifierData *md = ED_object_modifier_add(NULL, scene, ob, NULL, eModifierType_Armature);
((ArmatureModifierData *)md)->object = ob_arm;
tm->decompose(bind_shape_matrix, ob->loc, ob->rot, NULL, ob->size); tm->decompose(bind_shape_matrix, ob->loc, ob->rot, NULL, ob->size);
ob->parent = ob_arm; ob->parent = ob_arm;
ob->partype = PARSKEL; ob->partype = PAROBJECT;
what_does_parent(scene, ob, &workob);
invert_m4_m4(ob->parentinv, workob.obmat);
ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA; ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA;
((bArmature*)ob_arm->data)->deformflag = ARM_DEF_VGROUP; ((bArmature*)ob_arm->data)->deformflag = ARM_DEF_VGROUP;
// we need armature matrix here... where do we get it from I wonder...
// root node/joint? or node with <instance_controller>?
float parmat[4][4];
unit_m4(parmat);
invert_m4_m4(ob->parentinv, parmat);
// create all vertex groups // create all vertex groups
std::vector<JointData>::iterator it; std::vector<JointData>::iterator it;
int joint_index; int joint_index;
@ -551,7 +558,7 @@ private:
} }
} }
DAG_scene_sort(CTX_data_scene(C)); DAG_scene_sort(scene);
DAG_ids_flush_update(0); DAG_ids_flush_update(0);
WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
} }