Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/collada/AnimationExporter.cpp515
-rw-r--r--source/blender/collada/AnimationExporter.h25
-rw-r--r--source/blender/collada/AnimationImporter.cpp1
-rw-r--r--source/blender/collada/ExportSettings.h1
-rw-r--r--source/blender/collada/collada.cpp2
-rw-r--r--source/blender/collada/collada.h1
-rw-r--r--source/blender/collada/collada_utils.cpp12
-rw-r--r--source/blender/collada/collada_utils.h3
-rw-r--r--source/blender/editors/io/io_collada.c13
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c5
-rw-r--r--tests/python/CMakeLists.txt2
-rw-r--r--tests/python/collada/CMakeLists.txt65
-rw-r--r--tests/python/collada/mesh/test_mesh_simple.py144
13 files changed, 688 insertions, 101 deletions
diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp
index bd5cb05a1fa..759fb1546c7 100644
--- a/source/blender/collada/AnimationExporter.cpp
+++ b/source/blender/collada/AnimationExporter.cpp
@@ -49,38 +49,210 @@ bool AnimationExporter::exportAnimations(Scene *sce)
return has_animations;
}
-// called for each exported object
+
+/*
+ * This function creates a complete LINEAR Collada <Animation> Entry with all needed
+ * <source>, <sampler>, and <channel> entries.
+ * This is is used for creating sampled Transformation Animations for either:
+ *
+ * 1-axis animation:
+ * times contains the time points in seconds from within the timeline
+ * values contains the data (list of single floats)
+ * channel_count = 1
+ * axis_name = ['X' | 'Y' | 'Z']
+ * is_rot indicates if the animation is a rotation
+ *
+ * 3-axis animation:
+ * times contains the time points in seconds from within the timeline
+ * values contains the data (list of floats where each 3 entries are one vector)
+ * channel_count = 3
+ * axis_name = "" (actually not used)
+ * is_rot = false (see xxx below)
+ *
+ * xxx: I tried to create a 3 axis rotation animation
+ * like for translation or scale. But i could not
+ * figure out how to setup the channel for this case.
+ * So for now rotations are exported as 3 separate 1-axis collada animations
+ * See export_sampled_animation() further down.
+ */
+void AnimationExporter::create_sampled_animation(int channel_count,
+ std::vector<float> &times,
+ std::vector<float> &values,
+ std::string ob_name,
+ std::string label,
+ std::string axis_name,
+ bool is_rot)
+{
+
+ char anim_id[200];
+
+ BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), label.c_str(), axis_name.c_str());
+
+ openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING);
+
+ /* create input source */
+ std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, times, false, anim_id, "");
+
+ /* create output source */
+ std::string output_id;
+ if (channel_count == 1)
+ output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, &values[0], values.size(), is_rot, anim_id, axis_name.c_str());
+ else if(channel_count = 3)
+ output_id = create_xyz_source(&values[0], times.size(), anim_id);
+
+ 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 (LINEAR) */
+ std::string interpolation_id = fake_interpolation_source(times.size(), anim_id, "");
+
+ /* Create Sampler */
+ sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
+ addSampler(sampler);
+
+ /* Create channel */
+ std::string target = translate_id(ob_name) + "/" + label + axis_name + ((is_rot) ? ".ANGLE" : "");
+ addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+ closeAnimation();
+
+}
+
+/*
+ * Export all animation FCurves of an Object.
+ *
+ * Note: This uses the keyframes as sample points,
+ * and exports "baked keyframes" while keeping the tangent infromation
+ * of the FCurves intact. This works for simple cases, but breaks
+ * especially when negative scales are involved in the animation.
+ *
+ * If it is necessary to conserve the Animation precisely then
+ * use export_sampled_animation_set() instead.
+ */
+void AnimationExporter::export_keyframed_animation_set(Object *ob)
+{
+ FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
+
+ char *transformName;
+ while (fcu) {
+ //for armature animations as objects
+ if (ob->type == OB_ARMATURE)
+ transformName = fcu->rna_path;
+ else
+ transformName = extract_transform_name(fcu->rna_path);
+
+ if (
+ STREQ(transformName, "location") ||
+ STREQ(transformName, "scale") ||
+ (STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
+ STREQ(transformName, "rotation_quaternion"))
+ {
+ create_keyframed_animation(ob, fcu, transformName, false);
+ }
+ fcu = fcu->next;
+ }
+
+}
+
+/*
+ * Export the sampled animation of an Object.
+ *
+ * Note: This steps over all animation frames (step size is given in export_settings.sample_size)
+ * and then evaluates the transformation,
+ * and exports "baked samples" This works always, however currently the interpolation type is set
+ * to LINEAR for now. (maybe later this can be changed to BEZIER)
+ *
+ * Note: If it is necessary to keep the FCurves intact, then use export_keyframed_animation_set() instead.
+ * However be aware that exporting keyframed animation may modify the animation slightly.
+ * Also keyframed animation exports tend to break when negative scales are involved.
+ */
+void AnimationExporter::export_sampled_animation_set(Object *ob)
+{
+ static int LOC = 0;
+ static int EULX = 1;
+ static int EULY = 2;
+ static int EULZ = 3;
+ static int SCALE = 4;
+ static int TIME = 5;
+
+ if (this->export_settings->sampling_rate < 1)
+ return; // to avoid infinite loop
+
+ std::vector<float> baked_curves[6];
+ std::vector<float> &ctimes = baked_curves[TIME];
+ find_sampleframes(ob, ctimes);
+
+ for (std::vector<float>::iterator ctime = ctimes.begin(); ctime != ctimes.end(); ++ctime ) {
+ float fmat[4][4];
+ float floc[3];
+ float fquat[4];
+ float fsize[3];
+ float feul[3];
+
+ evaluate_anim_with_constraints(ob, *ctime); // set object transforms to the frame
+
+ BKE_object_matrix_local_get(ob, fmat);
+ mat4_decompose(floc, fquat, fsize, fmat);
+ quat_to_eul(feul, fquat);
+
+ baked_curves[LOC].push_back(floc[0]);
+ baked_curves[LOC].push_back(floc[1]);
+ baked_curves[LOC].push_back(floc[2]);
+
+ baked_curves[EULX].push_back(feul[0]);
+ baked_curves[EULY].push_back(feul[1]);
+ baked_curves[EULZ].push_back(feul[2]);
+
+ baked_curves[SCALE].push_back(fsize[0]);
+ baked_curves[SCALE].push_back(fsize[1]);
+ baked_curves[SCALE].push_back(fsize[2]);
+
+ }
+
+ std::string ob_name = id_name(ob);
+
+ create_sampled_animation(3, baked_curves[TIME], baked_curves[SCALE], ob_name, "scale", "", false);
+ create_sampled_animation(3, baked_curves[TIME], baked_curves[LOC], ob_name, "location", "", false);
+
+ /* Not sure how to export rotation as a 3channel animation,
+ * so separate into 3 single animations for now:
+ */
+
+ create_sampled_animation(1, baked_curves[TIME], baked_curves[EULX], ob_name, "rotation", "X", true);
+ create_sampled_animation(1, baked_curves[TIME], baked_curves[EULY], ob_name, "rotation", "Y", true);
+ create_sampled_animation(1, baked_curves[TIME], baked_curves[EULZ], ob_name, "rotation", "Z", true);
+
+ fprintf(stdout, "Animation Export: Baked %zd frames for %s (sampling rate: %d)\n",
+ baked_curves[0].size(),
+ ob->id.name,
+ this->export_settings->sampling_rate);
+}
+
+/* called for each exported object */
void AnimationExporter::operator()(Object *ob)
{
- FCurve *fcu;
char *transformName;
+
/* bool isMatAnim = false; */ /* UNUSED */
//Export transform animations
if (ob->adt && ob->adt->action) {
- fcu = (FCurve *)ob->adt->action->curves.first;
- //transform matrix export for bones are temporarily disabled here.
if (ob->type == OB_ARMATURE) {
bArmature *arm = (bArmature *)ob->data;
for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next)
write_bone_animation_matrix(ob, bone);
}
-
- while (fcu) {
- //for armature animations as objects
- if (ob->type == OB_ARMATURE)
- transformName = fcu->rna_path;
- else
- transformName = extract_transform_name(fcu->rna_path);
-
- if ((STREQ(transformName, "location") || STREQ(transformName, "scale")) ||
- (STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
- (STREQ(transformName, "rotation_quaternion")))
- {
- dae_animation(ob, fcu, transformName, false);
+ else {
+ if (this->export_settings->sampling_rate == -1) {
+ export_keyframed_animation_set(ob);
+ }
+ else {
+ export_sampled_animation_set(ob);
}
- fcu = fcu->next;
}
}
@@ -92,14 +264,14 @@ void AnimationExporter::operator()(Object *ob)
//Export Lamp parameter animations
if ( (ob->type == OB_LAMP) && ((Lamp *)ob->data)->adt && ((Lamp *)ob->data)->adt->action) {
- fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
+ FCurve *fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
while (fcu) {
transformName = extract_transform_name(fcu->rna_path);
if ((STREQ(transformName, "color")) || (STREQ(transformName, "spot_size")) ||
(STREQ(transformName, "spot_blend")) || (STREQ(transformName, "distance")))
{
- dae_animation(ob, fcu, transformName, true);
+ create_keyframed_animation(ob, fcu, transformName, true);
}
fcu = fcu->next;
}
@@ -107,7 +279,7 @@ void AnimationExporter::operator()(Object *ob)
//Export Camera parameter animations
if ( (ob->type == OB_CAMERA) && ((Camera *)ob->data)->adt && ((Camera *)ob->data)->adt->action) {
- fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first);
+ FCurve *fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first);
while (fcu) {
transformName = extract_transform_name(fcu->rna_path);
@@ -116,7 +288,7 @@ void AnimationExporter::operator()(Object *ob)
(STREQ(transformName, "clip_end")) ||
(STREQ(transformName, "clip_start")))
{
- dae_animation(ob, fcu, transformName, true);
+ create_keyframed_animation(ob, fcu, transformName, true);
}
fcu = fcu->next;
}
@@ -128,7 +300,7 @@ void AnimationExporter::operator()(Object *ob)
if (!ma) continue;
if (ma->adt && ma->adt->action) {
/* isMatAnim = true; */
- fcu = (FCurve *)ma->adt->action->curves.first;
+ FCurve *fcu = (FCurve *)ma->adt->action->curves.first;
while (fcu) {
transformName = extract_transform_name(fcu->rna_path);
@@ -136,7 +308,7 @@ void AnimationExporter::operator()(Object *ob)
(STREQ(transformName, "diffuse_color")) || (STREQ(transformName, "alpha")) ||
(STREQ(transformName, "ior")))
{
- dae_animation(ob, fcu, transformName, true, ma);
+ create_keyframed_animation(ob, fcu, transformName, true, ma);
}
fcu = fcu->next;
}
@@ -167,7 +339,7 @@ void AnimationExporter::export_morph_animation(Object *ob)
while (fcu) {
transformName = extract_transform_name(fcu->rna_path);
- dae_animation(ob, fcu, transformName, true);
+ create_keyframed_animation(ob, fcu, transformName, true);
fcu = fcu->next;
}
@@ -200,7 +372,7 @@ void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<fl
obtar = ct->tar;
if (obtar)
- find_frames(obtar, frames);
+ find_keyframes(obtar, frames);
}
if (cti->flush_constraint_targets)
@@ -265,8 +437,8 @@ std::string AnimationExporter::getAnimationPathId(const FCurve *fcu)
return translate_id(rna_path);
}
-//convert f-curves to animation curves and write
-void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
+/* convert f-curves to animation curves and write */
+void AnimationExporter::create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
{
const char *axis_name = NULL;
char anim_id[200];
@@ -274,6 +446,8 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
bool has_tangents = false;
bool quatRotation = false;
+ Object *obj = NULL;
+
if (STREQ(transformName, "rotation_quaternion") ) {
fprintf(stderr, "quaternion rotation curves are not supported. rotation curve will not be exported\n");
quatRotation = true;
@@ -291,15 +465,26 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
axis_name = axis_names[fcu->array_index];
}
- //axis names for transforms
- else if (STREQ(transformName, "location") ||
- STREQ(transformName, "scale") ||
- STREQ(transformName, "rotation_euler") ||
- STREQ(transformName, "rotation_quaternion"))
+ /*
+ * Note: Handle transformation animations separately (to apply matrix inverse to fcurves)
+ * We will use the object to evaluate the animation on all keyframes and calculate the
+ * resulting object matrix. We need this to incorporate the
+ * effects of the parent inverse matrix (when it contains a rotation component)
+ *
+ * TODO: try to combine exported fcurves into 3 channel animations like done
+ * in export_sampled_animation(). For now each channel is exported as separate <Animation>.
+ */
+
+ else if (
+ STREQ(transformName, "scale") ||
+ STREQ(transformName, "location") ||
+ STREQ(transformName, "rotation_euler"))
{
const char *axis_names[] = {"X", "Y", "Z"};
- if (fcu->array_index < 3)
+ if (fcu->array_index < 3) {
axis_name = axis_names[fcu->array_index];
+ obj = ob;
+ }
}
else {
/* no axis name. single parameter */
@@ -308,7 +493,7 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
std::string ob_name = std::string("null");
- //Create anim Id
+ /* Create anim Id */
if (ob->type == OB_ARMATURE) {
ob_name = getObjectBoneName(ob, fcu);
BLI_snprintf(
@@ -357,7 +542,7 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
output_id = create_lens_source_from_fcurve((Camera *) ob->data, COLLADASW::InputSemantic::OUTPUT, fcu, anim_id);
}
else {
- output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name);
+ output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name, obj);
}
// create interpolations source
@@ -369,10 +554,10 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
if (has_tangents) {
// create in_tangent source
- intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name);
+ intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name, obj);
// create out_tangent source
- outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name);
+ outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name, obj);
}
std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
@@ -475,8 +660,11 @@ void AnimationExporter::sample_and_write_bone_animation_matrix(Object *ob_arm, B
if (!pchan)
return;
- //every inserted keyframe of bones.
- find_frames(ob_arm, fra);
+
+ if (this->export_settings->sampling_rate < 1)
+ find_keyframes(ob_arm, fra);
+ else
+ find_sampleframes(ob_arm, fra);
if (flag & ARM_RESTPOS) {
arm->flag &= ~ARM_RESTPOS;
@@ -755,14 +943,60 @@ void AnimationExporter::get_source_values(BezTriple *bezt, COLLADASW::InputSeman
}
}
+// old function to keep compatibility for calls where offset and object are not needed
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);
+ return create_source_from_fcurve(semantic, fcu, anim_id, axis_name, NULL);
+}
- //bool is_angle = STREQ(fcu->rna_path, "rotation");
- bool is_angle = false;
+void AnimationExporter::evaluate_anim_with_constraints(Object *ob, float ctime)
+{
+ BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL);
+ ListBase *conlist = get_active_constraints(ob);
+ bConstraint *con;
+ for (con = (bConstraint *)conlist->first; con; con = con->next) {
+ ListBase targets = { NULL, NULL };
+
+ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
+
+ if (cti && cti->get_constraint_targets) {
+ bConstraintTarget *ct;
+ Object *obtar;
+ cti->get_constraint_targets(con, &targets);
+ for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
+ obtar = ct->tar;
+
+ if (obtar) {
+ BKE_animsys_evaluate_animdata(scene, &obtar->id, obtar->adt, ctime, ADT_RECALC_ANIM);
+ BKE_object_where_is_calc_time(scene, obtar, ctime);
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 1);
+ }
+ }
+ BKE_object_where_is_calc_time(scene, ob, ctime);
+}
- if (strstr(fcu->rna_path, "rotation") || strstr(fcu->rna_path,"spot_size")) is_angle = true;
+/*
+ * ob is needed to aply parent inverse information to fcurve.
+ * TODO: Here we have to step over all keyframes for each object and for each fcurve.
+ * Instead of processing each fcurve one by one,
+ * step over the animation from keyframe to keyframe,
+ * then create adjusted fcurves (and entries) for all affected objects.
+ * Then we would need to step through the scene only once.
+ */
+std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name, Object *ob)
+{
+ std::string source_id = anim_id + get_semantic_suffix(semantic);
+
+ bool is_angle = (strstr(fcu->rna_path, "rotation") || strstr(fcu->rna_path, "spot_size"));
+ bool is_euler = strstr(fcu->rna_path, "rotation_euler");
+ bool is_translation = strstr(fcu->rna_path, "location");
+ bool is_scale = strstr(fcu->rna_path, "scale");
+ bool is_tangent = false;
+ int offset_index = 0;
COLLADASW::FloatSourceF source(mSW);
source.setId(source_id);
@@ -773,27 +1007,76 @@ std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemanti
case COLLADASW::InputSemantic::INPUT:
case COLLADASW::InputSemantic::OUTPUT:
source.setAccessorStride(1);
+ offset_index = 0;
break;
case COLLADASW::InputSemantic::IN_TANGENT:
case COLLADASW::InputSemantic::OUT_TANGENT:
source.setAccessorStride(2);
+ offset_index = 1;
+ is_tangent = true;
break;
default:
break;
}
-
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
add_source_parameters(param, semantic, is_angle, axis_name, false);
source.prepareToAppendValues();
- for (unsigned int i = 0; i < fcu->totvert; i++) {
+ for (unsigned int frame_index = 0; frame_index < fcu->totvert; frame_index++) {
+ float fixed_val = 0;
+ if (ob) {
+ float fmat[4][4];
+ float frame = fcu->bezt[frame_index].vec[1][0];
+ float ctime = BKE_scene_frame_get_from_ctime(scene, frame);
+
+ evaluate_anim_with_constraints(ob, ctime); // set object transforms to fcurve's i'th keyframe
+
+ BKE_object_matrix_local_get(ob, fmat);
+ float floc[3];
+ float fquat[4];
+ float fsize[3];
+ mat4_decompose(floc, fquat, fsize, fmat);
+
+ if (is_euler) {
+ float eul[3];
+ quat_to_eul(eul, fquat);
+ fixed_val = RAD2DEGF(eul[fcu->array_index]);
+ }
+ else if (is_translation) {
+ fixed_val = floc[fcu->array_index];
+ }
+ else if (is_scale) {
+ fixed_val = fsize[fcu->array_index];
+ }
+ }
+
float values[3]; // be careful!
+ float offset = 0;
int length = 0;
- get_source_values(&fcu->bezt[i], semantic, is_angle, values, &length);
- for (int j = 0; j < length; j++)
- source.appendValues(values[j]);
+ get_source_values(&fcu->bezt[frame_index], semantic, is_angle, values, &length);
+ if (is_tangent) {
+ float bases[3];
+ int len = 0;
+ get_source_values(&fcu->bezt[frame_index], COLLADASW::InputSemantic::OUTPUT, is_angle, bases, &len);
+ offset = values[offset_index] - bases[0];
+ }
+
+ for (int j = 0; j < length; j++) {
+ float val;
+ if (j == offset_index) {
+ if (ob) {
+ val = fixed_val + offset;
+ }
+ else {
+ val = values[j] + offset;
+ }
+ } else {
+ val = values[j];
+ }
+ source.appendValues(val);
+ }
}
source.finish();
@@ -837,9 +1120,9 @@ std::string AnimationExporter::create_lens_source_from_fcurve(Camera *cam, COLLA
return source_id;
}
-
-
-//Currently called only to get OUTPUT source values ( if rotation and hence the axis is also specified )
+/*
+ * only to get OUTPUT source values ( if rotation and hence the axis is also specified )
+ */
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);
@@ -869,7 +1152,10 @@ std::string AnimationExporter::create_source_from_array(COLLADASW::InputSemantic
return source_id;
}
-// only used for sources with INPUT semantic
+
+/*
+ * only used for sources with INPUT semantic
+ */
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);
@@ -935,9 +1221,10 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
int j = 0;
for (it = frames.begin(); it != frames.end(); it++) {
float mat[4][4], ipar[4][4];
+ float frame = *it;
- float ctime = BKE_scene_frame_get_from_ctime(scene, *it);
- CFRA = BKE_scene_frame_get_from_ctime(scene, *it);
+ float ctime = BKE_scene_frame_get_from_ctime(scene, frame);
+ CFRA = BKE_scene_frame_get_from_ctime(scene, frame);
//BKE_scene_update_for_newframe(G.main->eval_ctx, G.main,scene,scene->lay);
BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL);
@@ -959,9 +1246,10 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
else
copy_m4_m4(mat, pchan->pose_mat);
- // OPEN_SIM_COMPATIBILITY
- // AFAIK animation to second life is via BVH, but no
- // reason to not have the collada-animation be correct
+ /* OPEN_SIM_COMPATIBILITY
+ * AFAIK animation to second life is via BVH, but no
+ * reason to not have the collada-animation be correct
+ */
if (export_settings->open_sim) {
float temp[4][4];
copy_m4_m4(temp, bone->arm_mat);
@@ -980,7 +1268,11 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
}
else {
- calc_ob_mat_at_time(ob, ctime, mat);
+ BKE_scene_frame_set(scene, ctime);
+ Main *bmain = bc_get_main();
+ EvaluationContext *ev_context = bc_get_evaluation_context();
+ BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay);
+ copy_m4_m4(mat, ob->obmat);
}
UnitConverter converter;
@@ -1008,7 +1300,9 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
}
-// only used for sources with OUTPUT semantic ( locations and scale)
+/*
+ * only used for sources with OUTPUT semantic ( locations and scale)
+ */
std::string AnimationExporter::create_xyz_source(float *v, int tot, const std::string& anim_id)
{
COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT;
@@ -1192,8 +1486,10 @@ std::string AnimationExporter::get_camera_param_sid(char *rna_path, int tm_type,
return std::string("");
}
-// Assign sid of the animated parameter or transform
-// for rotation, axis name is always appended and the value of append_axis is ignored
+/*
+ * Assign sid of the animated parameter or transform 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;
@@ -1277,29 +1573,10 @@ char *AnimationExporter::extract_transform_name(char *rna_path)
return dot ? (dot + 1) : rna_path;
}
-//find keyframes of all the objects animations
-void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra)
-{
- if (ob->adt && ob->adt->action) {
- FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
-
- for (; fcu; fcu = fcu->next) {
- 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());
- }
-}
-
-
-
-// enable fcurves driving a specific bone, disable all the rest
-// if bone_name = NULL enable all fcurves
+/*
+ * 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;
@@ -1364,14 +1641,53 @@ bool AnimationExporter::hasAnimations(Scene *sce)
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");
+ find_keyframes(ob, fra, prefix, "rotation_euler");
else if (rotmode == ROT_MODE_QUAT)
- find_frames(ob, fra, prefix, "rotation_quaternion");
+ find_keyframes(ob, fra, prefix, "rotation_quaternion");
/*else if (rotmode == ROT_MODE_AXISANGLE)
;*/
}
-void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name)
+/* Take care to always have the first frame and the last frame in the animation
+ * regardless of the sampling_rate setting
+ */
+void AnimationExporter::find_sampleframes(Object *ob, std::vector<float> &fra)
+{
+ int frame = scene->r.sfra;
+ do {
+ float ctime = BKE_scene_frame_get_from_ctime(scene, frame);
+ fra.push_back(ctime);
+ if (frame == scene->r.efra)
+ break;
+ frame += this->export_settings->sampling_rate;
+ if (frame > scene->r.efra)
+ frame = scene->r.efra; // make sure the last frame is always exported
+
+ } while (true);
+}
+
+/*
+ * find keyframes of all the objects animations
+ */
+void AnimationExporter::find_keyframes(Object *ob, std::vector<float> &fra)
+{
+ if (ob->adt && ob->adt->action) {
+ FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
+
+ for (; fcu; fcu = fcu->next) {
+ 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_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name)
{
if (ob->adt && ob->adt->action) {
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
@@ -1429,10 +1745,10 @@ void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bo
find_rotation_frames(ob_arm, fra, prefix, pchan->rotmode);
break;
case 1:
- find_frames(ob_arm, fra, prefix, "scale");
+ find_keyframes(ob_arm, fra, prefix, "scale");
break;
case 2:
- find_frames(ob_arm, fra, prefix, "location");
+ find_keyframes(ob_arm, fra, prefix, "location");
break;
default:
return;
@@ -1539,8 +1855,23 @@ bool AnimationExporter::validateConstraints(bConstraint *con)
return valid;
}
-void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[][4])
+#if 0
+/*
+ * Needed for sampled animations.
+ * This function calculates the object matrix at a given time,
+ * also taking constraints into account.
+ *
+ * XXX: Why looking at the constraints here is necessary?
+ * Maybe this can be done better?
+ */
+void AnimationExporter::calc_obmat_at_time(Object *ob, float ctime )
{
+ BKE_scene_frame_set(scene, ctime);
+
+ Main *bmain = bc_get_main();
+ EvaluationContext *ev_context = bc_get_evaluation_context();
+ BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay);
+
ListBase *conlist = get_active_constraints(ob);
bConstraint *con;
for (con = (bConstraint *)conlist->first; con; con = con->next) {
@@ -1566,6 +1897,6 @@ void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[
}
}
BKE_object_where_is_calc_time(scene, ob, ctime);
- copy_m4_m4(mat, ob->obmat);
}
+#endif
diff --git a/source/blender/collada/AnimationExporter.h b/source/blender/collada/AnimationExporter.h
index 4736361ad13..b26b0427f33 100644
--- a/source/blender/collada/AnimationExporter.h
+++ b/source/blender/collada/AnimationExporter.h
@@ -90,9 +90,11 @@ private:
public:
AnimationExporter(COLLADASW::StreamWriter *sw, const ExportSettings *export_settings):
- COLLADASW::LibraryAnimations(sw), export_settings(export_settings)
- { this->sw = sw; }
-
+ COLLADASW::LibraryAnimations(sw),
+ export_settings(export_settings)
+ {
+ this->sw = sw;
+ }
bool exportAnimations(Scene *sce);
@@ -102,7 +104,6 @@ public:
protected:
const ExportSettings *export_settings;
- void dae_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma = NULL);
void export_object_constraint_animation(Object *ob);
@@ -143,7 +144,15 @@ protected:
float* get_eul_source_for_quat(Object *ob );
+ void export_keyframed_animation_set(Object *ob);
+ void create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma = NULL);
+ void export_sampled_animation_set(Object *ob);
+ void create_sampled_animation(int channel_count, std::vector<float> &times, std::vector<float> &values, std::string, std::string label, std::string axis_name, bool is_rot);
+
+ void evaluate_anim_with_constraints(Object *ob, float ctime);
+
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_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name, Object *ob);
std::string create_lens_source_from_fcurve(Camera *cam, COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id);
@@ -164,8 +173,10 @@ protected:
std::string get_light_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
std::string get_camera_param_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_frames(Object *ob, std::vector<float> &fra);
+ void find_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name);
+ void find_keyframes(Object *ob, std::vector<float> &fra);
+ void find_sampleframes(Object *ob, std::vector<float> &fra);
+
void make_anim_frames_from_targets(Object *ob, std::vector<float> &frames );
@@ -186,6 +197,6 @@ protected:
bool validateConstraints(bConstraint *con);
- void calc_ob_mat_at_time(Object *ob, float ctime , float mat[][4]);
+ //void calc_obmat_at_time(Object *ob, float ctime);
};
diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp
index 95543b2dc18..bc91b94afd9 100644
--- a/source/blender/collada/AnimationImporter.cpp
+++ b/source/blender/collada/AnimationImporter.cpp
@@ -817,7 +817,6 @@ void AnimationImporter::apply_matrix_curves(Object *ob, std::vector<FCurve *>& a
}
float rot[4], loc[3], scale[3];
- transpose_m4(mat);
bc_rotate_from_reference_quat(rot, qref, mat);
copy_qt_qt(qref, rot);
diff --git a/source/blender/collada/ExportSettings.h b/source/blender/collada/ExportSettings.h
index 6d90edd2f67..3b0b5708c7c 100644
--- a/source/blender/collada/ExportSettings.h
+++ b/source/blender/collada/ExportSettings.h
@@ -39,6 +39,7 @@ public:
bool include_armatures;
bool include_shapekeys;
bool deform_bones_only;
+ int sampling_rate;
bool active_uv_only;
BC_export_texture_type export_texture_type;
diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp
index 024bc4a4a5c..79f50888150 100644
--- a/source/blender/collada/collada.cpp
+++ b/source/blender/collada/collada.cpp
@@ -78,6 +78,7 @@ int collada_export(Scene *sce,
int include_armatures,
int include_shapekeys,
int deform_bones_only,
+ int sampling_rate,
int active_uv_only,
BC_export_texture_type export_texture_type,
@@ -103,6 +104,7 @@ int collada_export(Scene *sce,
export_settings.include_armatures = include_armatures != 0;
export_settings.include_shapekeys = include_shapekeys != 0;
export_settings.deform_bones_only = deform_bones_only != 0;
+ export_settings.sampling_rate = sampling_rate;
export_settings.active_uv_only = active_uv_only != 0;
export_settings.export_texture_type = export_texture_type;
diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h
index d31f5a8ba62..a8f082f630c 100644
--- a/source/blender/collada/collada.h
+++ b/source/blender/collada/collada.h
@@ -77,6 +77,7 @@ int collada_export(struct Scene *sce,
int include_armatures,
int include_shapekeys,
int deform_bones_only,
+ int sampling_rate,
int active_uv_only,
BC_export_texture_type export_texture_type,
diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp
index 415daccfa3d..9f984a33ed1 100644
--- a/source/blender/collada/collada_utils.cpp
+++ b/source/blender/collada/collada_utils.cpp
@@ -53,6 +53,7 @@ extern "C" {
#include "BKE_mesh.h"
#include "BKE_scene.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_main.h"
#include "ED_armature.h"
@@ -132,6 +133,17 @@ int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space)
return true;
}
+Main *bc_get_main()
+{
+ return G.main;
+}
+
+EvaluationContext *bc_get_evaluation_context()
+{
+ Main *bmain = G.main;
+ return bmain->eval_ctx;
+}
+
Object *bc_add_object(Scene *scene, int type, const char *name)
{
Object *ob = BKE_object_add_only_object(G.main, type, name);
diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h
index e9066d7db46..8b5903b9746 100644
--- a/source/blender/collada/collada_utils.h
+++ b/source/blender/collada/collada_utils.h
@@ -63,6 +63,9 @@ extern "C" {
typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex *> > TexIndexTextureArrayMap;
+extern Main *bc_get_main();
+extern EvaluationContext *bc_get_evaluation_context();
+
extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index);
extern int bc_test_parent_loop(Object *par, Object *ob);
extern int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true);
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 39065606201..3746ba3b64a 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -86,6 +86,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
int include_armatures;
int include_shapekeys;
int deform_bones_only;
+ int sampling_rate;
int export_texture_type;
int use_texture_copies;
@@ -136,6 +137,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
include_children = RNA_boolean_get(op->ptr, "include_children");
include_armatures = RNA_boolean_get(op->ptr, "include_armatures");
include_shapekeys = RNA_boolean_get(op->ptr, "include_shapekeys");
+ sampling_rate = RNA_int_get(op->ptr, "sampling_rate");
deform_bones_only = RNA_boolean_get(op->ptr, "deform_bones_only");
export_texture_type = RNA_enum_get(op->ptr, "export_texture_type_selection");
@@ -165,6 +167,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
include_armatures,
include_shapekeys,
deform_bones_only,
+ sampling_rate,
active_uv_only,
export_texture_type,
@@ -229,6 +232,10 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(row, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
+
+
/* Texture options */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
@@ -251,6 +258,7 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
+
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "open_sim", 0, NULL, ICON_NONE);
@@ -366,7 +374,10 @@ void WM_OT_collada_export(wmOperatorType *ot)
"Export all Shape Keys from Mesh Objects");
RNA_def_boolean(func, "deform_bones_only", 0, "Deform Bones only",
- "Only export deforming bones with armatures");
+ "Only export deforming bones with armatures");
+
+ RNA_def_int(func, "sampling_rate", 0, -1, INT_MAX,
+ "Samplintg Rate", "The maximum distance of frames between 2 keyframes. Disabled when value is -1", -1, INT_MAX);
RNA_def_boolean(func, "active_uv_only", 0, "Only Selected UV Map",
"Export only the selected UV Map");
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 0f22f8d3b0f..7d95fbf4068 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -278,6 +278,7 @@ static void rna_Scene_collada_export(
int include_armatures,
int include_shapekeys,
int deform_bones_only,
+ int sampling_rate,
int active_uv_only,
int export_texture_type,
int use_texture_copies,
@@ -301,6 +302,7 @@ static void rna_Scene_collada_export(
include_armatures,
include_shapekeys,
deform_bones_only,
+ sampling_rate,
active_uv_only,
export_texture_type,
@@ -400,6 +402,9 @@ void RNA_api_scene(StructRNA *srna)
RNA_def_boolean(func, "deform_bones_only", false,
"Deform Bones only", "Only export deforming bones with armatures");
+ RNA_def_int(func, "sampling_rate", 0, -1, INT_MAX,
+ "Samplintg Rate", "The maximum distance of frames between 2 keyframes. Disabled when value is -1", -1, INT_MAX);
+
RNA_def_boolean(func, "active_uv_only", false, "Only Selected UV Map", "Export only the selected UV Map");
RNA_def_int(func, "export_texture_type", 0, INT_MIN, INT_MAX,
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index e03d8c065e7..6419c3f36e2 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -616,3 +616,5 @@ if(WITH_ALEMBIC)
--with-legacy-depsgraph=${WITH_LEGACY_DEPSGRAPH}
)
endif()
+
+add_subdirectory(collada)
diff --git a/tests/python/collada/CMakeLists.txt b/tests/python/collada/CMakeLists.txt
new file mode 100644
index 00000000000..b42a91329f9
--- /dev/null
+++ b/tests/python/collada/CMakeLists.txt
@@ -0,0 +1,65 @@
+# ***** 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): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+# --env-system-scripts allows to run without the install target.
+
+# Use '--write-blend=/tmp/test.blend' to view output
+
+# Some tests are interesting but take too long to run
+# and don't give deterministic results
+set(USE_EXPERIMENTAL_TESTS FALSE)
+
+set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests)
+set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests)
+
+# ugh, any better way to do this on testing only?
+execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
+
+#~ if(NOT IS_DIRECTORY ${TEST_SRC_DIR})
+#~ message(FATAL_ERROR "CMake test directory not found!")
+#~ endif()
+
+# all calls to blender use this
+if(APPLE)
+ if(${CMAKE_GENERATOR} MATCHES "Xcode")
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup)
+ else()
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+ endif()
+else()
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+endif()
+
+# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
+# set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE})
+# set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} ${TEST_BLENDER_EXE_PARAMS} )
+
+# ------------------------------------------------------------------------------
+# GENERAL PYTHON CORRECTNESS TESTS
+macro (COLLADA_TEST module test_name)
+ add_test(
+ NAME collada_${test_name}
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/collada/${module}/${test_name}.blend
+ --python ${CMAKE_CURRENT_LIST_DIR}/${module}/test_${test_name}.py --
+ --testdir ${TEST_SRC_DIR}/collada/${module}
+ )
+endmacro()
+
+COLLADA_TEST(mesh mesh_simple)
diff --git a/tests/python/collada/mesh/test_mesh_simple.py b/tests/python/collada/mesh/test_mesh_simple.py
new file mode 100644
index 00000000000..4ef98fdc6b4
--- /dev/null
+++ b/tests/python/collada/mesh/test_mesh_simple.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ##### 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# Call as follows:
+# python collada_mesh_simple.py --blender PATH_TO_BLENDER_EXE --testdir PATH_TO_SVN/lib/tests/collada/mesh
+#
+import sys
+import bpy
+import argparse
+import functools
+import shutil
+import tempfile
+import unittest
+import difflib
+import pathlib
+from pathlib import Path
+
+def with_tempdir(wrapped):
+ """Creates a temporary directory for the function, cleaning up after it returns normally.
+
+ When the wrapped function raises an exception, the contents of the temporary directory
+ remain available for manual inspection.
+
+ The wrapped function is called with an extra positional argument containing
+ the pathlib.Path() of the temporary directory.
+ """
+
+ @functools.wraps(wrapped)
+ def decorator(*args, **kwargs):
+ dirname = tempfile.mkdtemp(prefix='blender-collada-test')
+ #print("Using tempdir %s" % dirname)
+ try:
+ retval = wrapped(*args, pathlib.Path(dirname), **kwargs)
+ except:
+ print('Exception in %s, not cleaning up temporary directory %s' % (wrapped, dirname))
+ raise
+ else:
+ shutil.rmtree(dirname)
+ return retval
+
+ return decorator
+
+class AbstractColladaTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.testdir = pathlib.Path(args.testdir)
+
+ def checkdae(self, reference, export):
+ """
+ collada verifier checks if exported dae file is the same as reference dae
+ """
+
+ ref = open(reference)
+ exp = open(export)
+ diff=difflib.unified_diff(ref.readlines(), exp.readlines(), lineterm='', n=0)
+ ref.close()
+ exp.close()
+
+ diff_count = 0;
+ for line in diff:
+ error = True
+ for prefix in ('---', '+++', '@@'):
+ # Ignore diff metadata
+ if line.startswith(prefix):
+ error=False
+ break
+ else:
+ # Ignore time stamps
+ for ignore in ('<created>', '<modified>', '<authoring_tool>'):
+ if line[1:].strip().startswith(ignore):
+ error=False
+ break
+ if error:
+ diff_count +=1
+ print ("%s"%line.strip())
+
+ if diff_count > 0:
+ print("Generated file differs from reference")
+ print("reference: %s" % reference)
+ print("result : %s" % export)
+
+ return diff_count == 0
+
+class MeshExportTest(AbstractColladaTest):
+ @with_tempdir
+ def test_export_single_mesh(self, tempdir: pathlib.Path):
+ test = "mesh_simple_001"
+ reference_dae = self.testdir / Path("%s.dae" % test)
+ outfile = tempdir / Path("%s_out.dae" % test)
+
+ bpy.ops.wm.collada_export(filepath="%s" % str(outfile),
+ check_existing=True,
+ filemode=8,
+ display_type="DEFAULT",
+ sort_method="FILE_SORT_ALPHA",
+ apply_modifiers=False,
+ export_mesh_type=0,
+ export_mesh_type_selection="view",
+ selected=False,
+ include_children=False,
+ include_armatures=False,
+ include_shapekeys=True,
+ deform_bones_only=False,
+ sampling_rate=0,
+ active_uv_only=False,
+ use_texture_copies=True,
+ triangulate=False,
+ use_object_instantiation=True,
+ use_blender_profile=True,
+ sort_by_name=False,
+ export_transformation_type=0,
+ export_transformation_type_selection="matrix",
+ export_texture_type=0,
+ export_texture_type_selection="mat",
+ open_sim=False,
+ limit_precision=False,
+ keep_bind_info=False)
+
+ # Now check the resulting Collada file.
+ self.assertTrue(self.checkdae(reference_dae, outfile))
+
+if __name__ == '__main__':
+ sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--testdir', required=True)
+ args, remaining = parser.parse_known_args()
+ unittest.main(argv=sys.argv[0:1]+remaining) \ No newline at end of file