From dd7b9a362d9e362f81825bfa05f06741e9c1323b Mon Sep 17 00:00:00 2001 From: Gaia Clary Date: Sat, 24 Feb 2018 13:11:30 +0100 Subject: T45687: Rework the Export/Import of Animations This started with a fix for an animated Object Hierarchy. Then i decided to cleanup and optimize a bit. But at the end this has become a more or less full rewrite of the Animation Exporter. All of this happened in a separate local branch and i have retained all my local commits to better see what i have done. Brief description: * I fixed a few issues with exporting keyframed animations of object hierarchies where the objects have parent inverse matrices which differ from the Identity matrix. * I added the option to export sampled animations with a user defined sampling rate (new user interface option) * I briefly tested Object Animations and Rig Animations. What is still needed: * Cleanup the code * Optimize the user interface * Do the Documentation Reviewers: mont29 Reviewed By: mont29 Differential Revision: https://developer.blender.org/D3070 --- source/blender/collada/AnimationExporter.cpp | 515 ++++++++++++++++++++++----- 1 file changed, 423 insertions(+), 92 deletions(-) (limited to 'source/blender/collada/AnimationExporter.cpp') 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 Entry with all needed + * , , and 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 ×, + std::vector &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 baked_curves[6]; + std::vector &ctimes = baked_curves[TIME]; + find_sampleframes(ob, ctimes); + + for (std::vector::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::vectortar; 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 . + */ + + 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 ¶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(); @@ -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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 -- cgit v1.2.3