diff options
author | Gaia Clary <gaia.clary@machinimatrix.org> | 2018-02-25 02:06:41 +0300 |
---|---|---|
committer | Gaia Clary <gaia.clary@machinimatrix.org> | 2018-02-25 02:06:41 +0300 |
commit | 2b158861a3a516dd8f4dac73f8984230f02bc9de (patch) | |
tree | 9f53ceec22962a730b3ca50c1f252ec71c5a26e6 | |
parent | 94eb1897523d4cf464ea0cdf90ccbd7db90f997d (diff) | |
parent | d91f2ac37aa02d96a00d116fa55cdc9f55afd32c (diff) |
merged collada rework from master into blender 2.8
-rw-r--r-- | source/blender/collada/AnimationExporter.cpp | 517 | ||||
-rw-r--r-- | source/blender/collada/AnimationExporter.h | 25 | ||||
-rw-r--r-- | source/blender/collada/AnimationImporter.cpp | 1 | ||||
-rw-r--r-- | source/blender/collada/ExportSettings.h | 1 | ||||
-rw-r--r-- | source/blender/collada/collada.cpp | 16 | ||||
-rw-r--r-- | source/blender/collada/collada.h | 5 | ||||
-rw-r--r-- | source/blender/collada/collada_utils.cpp | 17 | ||||
-rw-r--r-- | source/blender/collada/collada_utils.h | 4 | ||||
-rw-r--r-- | source/blender/editors/io/io_collada.c | 18 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_scene_api.c | 15 | ||||
-rw-r--r-- | source/blenderplayer/bad_level_call_stubs/stubs.c | 1 | ||||
-rw-r--r-- | tests/python/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/python/collada/CMakeLists.txt | 65 | ||||
-rw-r--r-- | tests/python/collada/mesh/test_mesh_simple.py | 144 |
14 files changed, 713 insertions, 117 deletions
diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp index cc772535e37..24e5ecb8d10 100644 --- a/source/blender/collada/AnimationExporter.cpp +++ b/source/blender/collada/AnimationExporter.cpp @@ -50,38 +50,210 @@ bool AnimationExporter::exportAnimations(const struct EvaluationContext *eval_ct 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> ×, + 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; } } @@ -93,14 +265,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; } @@ -108,7 +280,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); @@ -117,7 +289,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; } @@ -129,7 +301,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); @@ -137,7 +309,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; } @@ -168,7 +340,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; } @@ -201,7 +373,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) @@ -266,8 +438,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]; @@ -275,6 +447,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; @@ -292,15 +466,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 */ @@ -309,7 +494,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( @@ -358,7 +543,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 @@ -370,10 +555,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; @@ -476,8 +661,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; @@ -756,14 +944,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(this->eval_ctx, ob); + bConstraint *con; + for (con = (bConstraint *)conlist->first; con; con = con->next) { + ListBase targets = { NULL, NULL }; - if (strstr(fcu->rna_path, "rotation") || strstr(fcu->rna_path,"spot_size")) is_angle = true; + 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(this->eval_ctx, scene, obtar, ctime); + } + } + + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 1); + } + } + BKE_object_where_is_calc_time(this->eval_ctx, scene, ob, ctime); +} + +/* + * 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); @@ -774,27 +1008,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 ¶m = 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(); @@ -838,9 +1121,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); @@ -870,7 +1153,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); @@ -936,6 +1222,7 @@ 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); @@ -960,9 +1247,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); @@ -981,7 +1269,10 @@ 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(); + BKE_animsys_evaluate_all_animation(bmain, scene, ctime); + copy_m4_m4(mat, ob->obmat); } UnitConverter converter; @@ -1009,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; @@ -1193,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; @@ -1278,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; @@ -1365,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; @@ -1430,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; @@ -1540,9 +1855,28 @@ 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 ) { +<<<<<<< HEAD ListBase *conlist = get_active_constraints(this->eval_ctx, ob); +======= + 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); +>>>>>>> master bConstraint *con; for (con = (bConstraint *)conlist->first; con; con = con->next) { ListBase targets = {NULL, NULL}; @@ -1566,7 +1900,12 @@ void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[ cti->flush_constraint_targets(con, &targets, 1); } } +<<<<<<< HEAD BKE_object_where_is_calc_time(eval_ctx, scene, ob, ctime); copy_m4_m4(mat, ob->obmat); +======= + BKE_object_where_is_calc_time(scene, ob, ctime); +>>>>>>> master } +#endif diff --git a/source/blender/collada/AnimationExporter.h b/source/blender/collada/AnimationExporter.h index 5af5d884455..3bb510e51d6 100644 --- a/source/blender/collada/AnimationExporter.h +++ b/source/blender/collada/AnimationExporter.h @@ -91,9 +91,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(const struct EvaluationContext *eval_ctx, Scene *sce); @@ -103,7 +105,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); @@ -144,7 +145,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> ×, 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); @@ -165,8 +174,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 ); @@ -187,6 +198,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 e63b70edcf5..48fb27bc533 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 33a7527f383..0483449e513 100644 --- a/source/blender/collada/ExportSettings.h +++ b/source/blender/collada/ExportSettings.h @@ -40,6 +40,7 @@ public: bool include_armatures; bool include_shapekeys; bool deform_bones_only; + int sampling_rate; bool active_uv_only; bool include_material_textures; diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index 6c5fb0b33f3..286444882e4 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -37,6 +37,7 @@ extern "C" { #include "BKE_scene.h" #include "BKE_context.h" +#include "DEG_depsgraph.h" /* make dummy file */ #include "BLI_fileops.h" @@ -67,9 +68,7 @@ int collada_import(bContext *C, return 0; } -int collada_export(const EvaluationContext *eval_ctx, - Scene *sce, - ViewLayer *view_layer, +int collada_export(bContext *C, const char *filepath, int apply_modifiers, @@ -80,6 +79,7 @@ int collada_export(const EvaluationContext *eval_ctx, int include_armatures, int include_shapekeys, int deform_bones_only, + int sampling_rate, int active_uv_only, int include_material_textures, @@ -96,6 +96,11 @@ int collada_export(const EvaluationContext *eval_ctx, { ExportSettings export_settings; + EvaluationContext eval_ctx; + CTX_data_eval_ctx(C, &eval_ctx); + Scene *sce = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + export_settings.filepath = (char *)filepath; export_settings.apply_modifiers = apply_modifiers != 0; @@ -105,6 +110,7 @@ int collada_export(const EvaluationContext *eval_ctx, 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.include_material_textures= include_material_textures != 0; @@ -124,7 +130,9 @@ int collada_export(const EvaluationContext *eval_ctx, if (export_settings.include_children) includeFilter |= OB_REL_CHILDREN_RECURSIVE; eObjectSet objectSet = (export_settings.selected) ? OB_SET_SELECTED : OB_SET_ALL; + export_settings.export_set = BKE_object_relational_superset(view_layer, objectSet, (eObRelationTypes)includeFilter); + int export_count = BLI_linklist_count(export_settings.export_set); if (export_count == 0) { @@ -141,7 +149,7 @@ int collada_export(const EvaluationContext *eval_ctx, } DocumentExporter exporter(&export_settings); - int status = exporter.exportCurrentScene(eval_ctx, sce); + int status = exporter.exportCurrentScene(&eval_ctx, sce); BLI_linklist_free(export_settings.export_set, NULL); diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index eb3964f9cb7..89853b8849f 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -64,9 +64,7 @@ int collada_import(struct bContext *C, int keep_bind_info); -int collada_export(const struct EvaluationContext *eval_ctx, - struct Scene *sce, - struct ViewLayer *view_layer, +int collada_export(struct bContext *C, const char *filepath, int apply_modifiers, BC_export_mesh_type export_mesh_type, @@ -76,6 +74,7 @@ int collada_export(const struct EvaluationContext *eval_ctx, int include_armatures, int include_shapekeys, int deform_bones_only, + int sampling_rate, int active_uv_only, int include_material_textures, diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp index 117e2ef7f76..99eca373e4e 100644 --- a/source/blender/collada/collada_utils.cpp +++ b/source/blender/collada/collada_utils.cpp @@ -51,6 +51,7 @@ extern "C" { #include "BKE_mesh.h" #include "BKE_scene.h" #include "BKE_DerivedMesh.h" +#include "BKE_main.h" #include "ED_armature.h" @@ -135,6 +136,22 @@ int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space) return true; } +Scene *bc_get_scene(bContext *C) +{ + return CTX_data_scene(C); +} + +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, ViewLayer *view_layer, 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 e3a16105861..43fcdf66dce 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -64,6 +64,10 @@ struct EvaluationContext; typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex *> > TexIndexTextureArrayMap; +extern Scene *bc_get_scene(bContext *C); +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 1b7fd319da0..98c88ba5fc9 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -91,6 +91,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 include_material_textures; int use_texture_copies; @@ -143,6 +144,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"); include_material_textures = RNA_boolean_get(op->ptr, "include_material_textures"); @@ -162,10 +164,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) /* get editmode results */ ED_object_editmode_load(CTX_data_edit_object(C)); - - export_count = collada_export(&eval_ctx, - CTX_data_scene(C), - CTX_data_view_layer(C), + export_count = collada_export(C, filepath, apply_modifiers, export_mesh_type, @@ -174,6 +173,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) include_armatures, include_shapekeys, deform_bones_only, + sampling_rate, active_uv_only, include_material_textures, @@ -238,6 +238,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); @@ -260,6 +264,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); @@ -369,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", diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index f99803a1d99..580b4d34106 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -296,7 +296,6 @@ static void rna_Scene_alembic_export( /* Note: This definition must match to the generated function call */ static void rna_Scene_collada_export( - Scene *scene, bContext *C, const char *filepath, int apply_modifiers, @@ -307,6 +306,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 include_material_textures, int use_texture_copies, @@ -319,13 +319,7 @@ static void rna_Scene_collada_export( int limit_precision, int keep_bind_info) { - EvaluationContext eval_ctx; - - CTX_data_eval_ctx(C, &eval_ctx); - - collada_export(&eval_ctx, - scene, - CTX_data_view_layer(C), + collada_export(C, filepath, apply_modifiers, @@ -336,6 +330,7 @@ static void rna_Scene_collada_export( include_armatures, include_shapekeys, deform_bones_only, + sampling_rate, active_uv_only, include_material_textures, @@ -415,6 +410,7 @@ void RNA_api_scene(StructRNA *srna) #ifdef WITH_COLLADA /* don't remove this, as COLLADA exporting cannot be done through operators in render() callback. */ func = RNA_def_function(srna, "collada_export", "rna_Scene_collada_export"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT); parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Collada file"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_property_subtype(parm, PROP_FILEPATH); /* allow non utf8 */ @@ -438,6 +434,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_boolean(func, "include_material_textures", false, diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index a0cb8ed8c98..28f413299a5 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -838,6 +838,7 @@ int collada_export(const struct EvaluationContext *eval_ctx, int include_armatures, int include_shapekeys, int deform_bones_only, + int sampling_rate, int active_uv_only, int include_material_textures, diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index f973488d657..20b65889c2b 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -618,3 +618,4 @@ if(WITH_ALEMBIC) endif() add_subdirectory(view_layer) +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 |