/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed. * * ***** END GPL LICENSE BLOCK ***** */ #include "GeometryExporter.h" #include "AnimationExporter.h" #include "MaterialExporter.h" template void forEachObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) { LinkNode *node; for (node = export_set; node; node = node->next) { Object *ob = (Object *)node->link; f(ob); } } bool AnimationExporter::exportAnimations(Scene *sce) { bool has_animations = hasAnimations(sce); if (has_animations) { this->scene = sce; openLibrary(); forEachObjectInExportSet(sce, *this, this->export_settings->export_set); closeLibrary(); } return has_animations; } bool AnimationExporter::is_flat_line(std::vector &values, int channel_count) { for (int i = 0; i < values.size(); i += channel_count) { for (int j = 0; j < channel_count; j++) { if (!bc_in_range(values[j], values[i+j], 0.000001)) return false; } } return true; } /* * 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]; if (is_flat_line(values, channel_count)) return; 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); else if (channel_count == 16) output_id = create_4x4_source(times, values, 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; if (!fcu) { return; /* object has no animation */ } if (this->export_settings->export_transformation_type == BC_TRANSFORMATION_TYPE_MATRIX) { std::vector ctimes; find_keyframes(ob, ctimes); if (ctimes.size() > 0) export_sampled_matrix_animation(ob, ctimes); } else { 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) { std::vectorctimes; find_sampleframes(ob, ctimes); if (ctimes.size() > 0) { if (this->export_settings->export_transformation_type == BC_TRANSFORMATION_TYPE_MATRIX) export_sampled_matrix_animation(ob, ctimes); else export_sampled_transrotloc_animation(ob, ctimes); } } void AnimationExporter::export_sampled_matrix_animation(Object *ob, std::vector &ctimes) { UnitConverter converter; std::vector values; for (std::vector::iterator ctime = ctimes.begin(); ctime != ctimes.end(); ++ctime) { float fmat[4][4]; bc_update_scene(scene, *ctime); BKE_object_matrix_local_get(ob, fmat); if (this->export_settings->limit_precision) bc_sanitize_mat(fmat, 6); for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) values.push_back(fmat[i][j]); } std::string ob_name = id_name(ob); create_sampled_animation(16, ctimes, values, ob_name, "transform", "", false); } void AnimationExporter::export_sampled_transrotloc_animation(Object *ob, std::vector &ctimes) { static int LOC = 0; static int EULX = 1; static int EULY = 2; static int EULZ = 3; static int SCALE = 4; std::vector baked_curves[5]; 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]; bc_update_scene(scene, *ctime); 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, ctimes, baked_curves[SCALE], ob_name, "scale", "", false); create_sampled_animation(3, ctimes, 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, ctimes, baked_curves[EULX], ob_name, "rotation", "X", true); create_sampled_animation(1, ctimes, baked_curves[EULY], ob_name, "rotation", "Y", true); create_sampled_animation(1, ctimes, baked_curves[EULZ], ob_name, "rotation", "Z", true); fprintf(stdout, "Animation Export: Baked %d frames for %s (sampling rate: %d)\n", (int)baked_curves[0].size(), ob->id.name, this->export_settings->sampling_rate); } /* called for each exported object */ void AnimationExporter::operator()(Object *ob) { char *transformName; /* bool isMatAnim = false; */ /* UNUSED */ //Export transform animations if (ob->adt && ob->adt->action) { if (ob->type == OB_ARMATURE) { /* Export skeletal animation (if any)*/ bArmature *arm = (bArmature *)ob->data; for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) write_bone_animation_matrix(ob, bone); } /* Armatures can have object animation and skeletal animation*/ if (this->export_settings->sampling_rate < 1) { export_keyframed_animation_set(ob); } else { export_sampled_animation_set(ob); } } export_object_constraint_animation(ob); //This needs to be handled by extra profiles, so postponed for now //export_morph_animation(ob); //Export Lamp parameter animations if ( (ob->type == OB_LAMP) && ((Lamp *)ob->data)->adt && ((Lamp *)ob->data)->adt->action) { 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"))) { create_keyframed_animation(ob, fcu, transformName, true); } fcu = fcu->next; } } //Export Camera parameter animations if ( (ob->type == OB_CAMERA) && ((Camera *)ob->data)->adt && ((Camera *)ob->data)->adt->action) { FCurve *fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first); while (fcu) { transformName = extract_transform_name(fcu->rna_path); if ((STREQ(transformName, "lens")) || (STREQ(transformName, "ortho_scale")) || (STREQ(transformName, "clip_end")) || (STREQ(transformName, "clip_start"))) { create_keyframed_animation(ob, fcu, transformName, true); } fcu = fcu->next; } } //Export Material parameter animations. for (int a = 0; a < ob->totcol; a++) { Material *ma = give_current_material(ob, a + 1); if (!ma) continue; if (ma->adt && ma->adt->action) { /* isMatAnim = true; */ FCurve *fcu = (FCurve *)ma->adt->action->curves.first; while (fcu) { transformName = extract_transform_name(fcu->rna_path); if ((STREQ(transformName, "specular_hardness")) || (STREQ(transformName, "specular_color")) || (STREQ(transformName, "diffuse_color")) || (STREQ(transformName, "alpha")) || (STREQ(transformName, "ior"))) { create_keyframed_animation(ob, fcu, transformName, true, ma); } fcu = fcu->next; } } } } void AnimationExporter::export_object_constraint_animation(Object *ob) { std::vector fra; //Takes frames of target animations make_anim_frames_from_targets(ob, fra); if (fra.size()) dae_baked_object_animation(fra, ob); } void AnimationExporter::export_morph_animation(Object *ob) { FCurve *fcu; char *transformName; Key *key = BKE_key_from_object(ob); if (!key) return; if (key->adt && key->adt->action) { fcu = (FCurve *)key->adt->action->curves.first; while (fcu) { transformName = extract_transform_name(fcu->rna_path); create_keyframed_animation(ob, fcu, transformName, true); fcu = fcu->next; } } } void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector &frames ) { ListBase *conlist = get_active_constraints(ob); if (conlist == NULL) return; bConstraint *con; for (con = (bConstraint *)conlist->first; con; con = con->next) { ListBase targets = {NULL, NULL}; const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); if (!validateConstraints(con)) continue; if (cti && cti->get_constraint_targets) { bConstraintTarget *ct; Object *obtar; /* get targets * - constraints should use ct->matrix, not directly accessing values * - ct->matrix members have not yet been calculated here! */ cti->get_constraint_targets(con, &targets); for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) { obtar = ct->tar; if (obtar) find_keyframes(obtar, frames); } if (cti->flush_constraint_targets) cti->flush_constraint_targets(con, &targets, 1); } } } //euler sources from quternion sources float *AnimationExporter::get_eul_source_for_quat(Object *ob) { FCurve *fcu = (FCurve *)ob->adt->action->curves.first; const int keys = fcu->totvert; float *quat = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 4, "quat output source values"); float *eul = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 3, "quat output source values"); float temp_quat[4]; float temp_eul[3]; while (fcu) { char *transformName = extract_transform_name(fcu->rna_path); if (STREQ(transformName, "rotation_quaternion") ) { for (int i = 0; i < fcu->totvert; i++) { *(quat + (i * 4) + fcu->array_index) = fcu->bezt[i].vec[1][1]; } } fcu = fcu->next; } for (int i = 0; i < keys; i++) { for (int j = 0; j < 4; j++) temp_quat[j] = quat[(i * 4) + j]; quat_to_eul(temp_eul, temp_quat); for (int k = 0; k < 3; k++) eul[i * 3 + k] = temp_eul[k]; } MEM_freeN(quat); return eul; } //Get proper name for bones std::string AnimationExporter::getObjectBoneName(Object *ob, const FCurve *fcu) { //hard-way to derive the bone name from rna_path. Must find more compact method std::string rna_path = std::string(fcu->rna_path); char *boneName = strtok((char *)rna_path.c_str(), "\""); boneName = strtok(NULL, "\""); if (boneName != NULL) return /*id_name(ob) + "_" +*/ std::string(boneName); else return id_name(ob); } std::string AnimationExporter::getAnimationPathId(const FCurve *fcu) { std::string rna_path = std::string(fcu->rna_path); return translate_id(rna_path); } /* 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]; 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; return; } //axis names for colors else if (STREQ(transformName, "color") || STREQ(transformName, "specular_color") || STREQ(transformName, "diffuse_color") || STREQ(transformName, "alpha")) { const char *axis_names[] = {"R", "G", "B"}; if (fcu->array_index < 3) axis_name = axis_names[fcu->array_index]; } /* * 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) { axis_name = axis_names[fcu->array_index]; obj = ob; } } else { /* no axis name. single parameter */ axis_name = ""; } std::string ob_name = std::string("null"); /* Create anim Id */ if (ob->type == OB_ARMATURE) { ob_name = getObjectBoneName(ob, fcu); BLI_snprintf( anim_id, sizeof(anim_id), "%s_%s.%s", (char *)translate_id(ob_name).c_str(), (char *)translate_id(transformName).c_str(), axis_name); } else { if (ma) ob_name = id_name(ob) + "_material"; else ob_name = id_name(ob); BLI_snprintf( anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), (char *)getAnimationPathId(fcu).c_str(), axis_name); } openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name); // create output source std::string output_id; //quat rotations are skipped for now, because of complications with determining axis. if (quatRotation) { float *eul = get_eul_source_for_quat(ob); float *eul_axis = (float *)MEM_callocN(sizeof(float) * fcu->totvert, "quat output source values"); for (int i = 0; i < fcu->totvert; i++) { eul_axis[i] = eul[i * 3 + fcu->array_index]; } output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, eul_axis, fcu->totvert, quatRotation, anim_id, axis_name); MEM_freeN(eul); MEM_freeN(eul_axis); } else if (STREQ(transformName, "lens") && (ob->type == OB_CAMERA)) { 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, obj); } // create interpolations source std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name, &has_tangents); // handle tangents (if required) std::string intangent_id; std::string outtangent_id; if (has_tangents) { // create in_tangent source 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, obj); } std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); std::string empty; sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); // this input is required sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); if (has_tangents) { sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT, COLLADABU::URI(empty, intangent_id)); sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT, COLLADABU::URI(empty, outtangent_id)); } addSampler(sampler); std::string target; if (!is_param) target = translate_id(ob_name) + "/" + get_transform_sid(fcu->rna_path, -1, axis_name, true); else { if (ob->type == OB_LAMP) target = get_light_id(ob) + "/" + get_light_param_sid(fcu->rna_path, -1, axis_name, true); if (ob->type == OB_CAMERA) target = get_camera_id(ob) + "/" + get_camera_param_sid(fcu->rna_path, -1, axis_name, true); if (ma) target = translate_id(id_name(ma)) + "-effect" + "/common/" /*profile common is only supported */ + get_transform_sid(fcu->rna_path, -1, axis_name, true); //if shape key animation, this is the main problem, how to define the channel targets. /*target = get_morph_id(ob) + "/value" +*/ } addChannel(COLLADABU::URI(empty, sampler_id), target); closeAnimation(); } //write bone animations in transform matrix sources void AnimationExporter::write_bone_animation_matrix(Object *ob_arm, Bone *bone) { if (!ob_arm->adt) return; //This will only export animations of bones in deform group. /* if (!is_bone_deform_group(bone)) return; */ sample_and_write_bone_animation_matrix(ob_arm, bone); for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) write_bone_animation_matrix(ob_arm, child); } bool AnimationExporter::is_bone_deform_group(Bone *bone) { bool is_def; //Check if current bone is deform if ((bone->flag & BONE_NO_DEFORM) == 0) return true; //Check child bones else { for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { //loop through all the children until deform bone is found, and then return is_def = is_bone_deform_group(child); if (is_def) return true; } } //no deform bone found in children also return false; } void AnimationExporter::sample_and_write_bone_animation_matrix(Object *ob_arm, Bone *bone) { bArmature *arm = (bArmature *)ob_arm->data; int flag = arm->flag; std::vector fra; //char prefix[256]; //Check if there is a fcurve in the armature for the bone in param //when baking this check is not needed, solve every bone for every frame. /*FCurve *fcu = (FCurve *)ob_arm->adt->action->curves.first; while (fcu) { std::string bone_name = getObjectBoneName(ob_arm, fcu); int val = BLI_strcasecmp((char *)bone_name.c_str(), bone->name); if (val == 0) break; fcu = fcu->next; } if (!(fcu)) return;*/ bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); if (!pchan) return; 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; BKE_pose_where_is(scene, ob_arm); } if (fra.size()) { dae_baked_animation(fra, ob_arm, bone); } if (flag & ARM_RESTPOS) arm->flag = flag; BKE_pose_where_is(scene, ob_arm); } void AnimationExporter::dae_baked_animation(std::vector &fra, Object *ob_arm, Bone *bone) { std::string ob_name = id_name(ob_arm); std::string bone_name = bone->name; char anim_id[200]; if (!fra.size()) return; BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), (char *)translate_id(bone_name).c_str(), "pose_matrix"); openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, fra, false, anim_id, ""); // create output source std::string output_id; output_id = create_4x4_source(fra, ob_arm, bone, anim_id); // create interpolations source std::string interpolation_id = fake_interpolation_source(fra.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 // this input is required sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); addSampler(sampler); std::string target = get_joint_id(bone, ob_arm) + "/transform"; addChannel(COLLADABU::URI(empty, sampler_id), target); closeAnimation(); } void AnimationExporter::dae_baked_object_animation(std::vector &fra, Object *ob) { std::string ob_name = id_name(ob); char anim_id[200]; if (!fra.size()) return; BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s", (char *)translate_id(ob_name).c_str(), "object_matrix"); openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, fra, false, anim_id, ""); // create output source std::string output_id; output_id = create_4x4_source( fra, ob, NULL, anim_id); // create interpolations source std::string interpolation_id = fake_interpolation_source(fra.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 // this input is required sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); addSampler(sampler); std::string target = translate_id(ob_name) + "/transform"; addChannel(COLLADABU::URI(empty, sampler_id), target); closeAnimation(); } // dae_bone_animation -> add_bone_animation // (blend this into dae_bone_animation) void AnimationExporter::dae_bone_animation(std::vector &fra, float *values, int tm_type, int axis, std::string ob_name, std::string bone_name) { const char *axis_names[] = {"X", "Y", "Z"}; const char *axis_name = NULL; char anim_id[200]; bool is_rot = tm_type == 0; if (!fra.size()) return; char rna_path[200]; BLI_snprintf(rna_path, sizeof(rna_path), "pose.bones[\"%s\"].%s", bone_name.c_str(), tm_type == 0 ? "rotation_quaternion" : (tm_type == 1 ? "scale" : "location")); if (axis > -1) axis_name = axis_names[axis]; std::string transform_sid = get_transform_sid(NULL, tm_type, axis_name, false); BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), (char *)translate_id(bone_name).c_str(), (char *)transform_sid.c_str()); openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, fra, is_rot, anim_id, axis_name); // create output source std::string output_id; if (axis == -1) output_id = create_xyz_source(values, fra.size(), anim_id); else output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, values, fra.size(), is_rot, anim_id, axis_name); // create interpolations source std::string interpolation_id = fake_interpolation_source(fra.size(), anim_id, axis_name); std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); std::string empty; sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id)); sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id)); // TODO create in/out tangents source // this input is required sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); addSampler(sampler); std::string target = translate_id(ob_name + "_" + bone_name) + "/" + transform_sid; addChannel(COLLADABU::URI(empty, sampler_id), target); closeAnimation(); } float AnimationExporter::convert_time(float frame) { return FRA2TIME(frame); } float AnimationExporter::convert_angle(float angle) { return COLLADABU::Math::Utils::radToDegF(angle); } std::string AnimationExporter::get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) { switch (semantic) { case COLLADASW::InputSemantic::INPUT: return INPUT_SOURCE_ID_SUFFIX; case COLLADASW::InputSemantic::OUTPUT: return OUTPUT_SOURCE_ID_SUFFIX; case COLLADASW::InputSemantic::INTERPOLATION: return INTERPOLATION_SOURCE_ID_SUFFIX; case COLLADASW::InputSemantic::IN_TANGENT: return INTANGENT_SOURCE_ID_SUFFIX; case COLLADASW::InputSemantic::OUT_TANGENT: return OUTTANGENT_SOURCE_ID_SUFFIX; default: break; } return ""; } void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis, bool transform) { switch (semantic) { case COLLADASW::InputSemantic::INPUT: param.push_back("TIME"); break; case COLLADASW::InputSemantic::OUTPUT: if (is_rot) { param.push_back("ANGLE"); } else { if (axis) { param.push_back(axis); } else if (transform) { param.push_back("TRANSFORM"); } else { //assumes if axis isn't specified all axises are added param.push_back("X"); param.push_back("Y"); param.push_back("Z"); } } break; case COLLADASW::InputSemantic::IN_TANGENT: case COLLADASW::InputSemantic::OUT_TANGENT: param.push_back("X"); param.push_back("Y"); break; default: break; } } void AnimationExporter::get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool is_angle, float *values, int *length) { switch (semantic) { case COLLADASW::InputSemantic::INPUT: *length = 1; values[0] = convert_time(bezt->vec[1][0]); break; case COLLADASW::InputSemantic::OUTPUT: *length = 1; if (is_angle) { values[0] = RAD2DEGF(bezt->vec[1][1]); } else { values[0] = bezt->vec[1][1]; } break; case COLLADASW::InputSemantic::IN_TANGENT: *length = 2; values[0] = convert_time(bezt->vec[0][0]); if (bezt->ipo != BEZT_IPO_BEZ) { // We're in a mixed interpolation scenario, set zero as it's irrelevant but value might contain unused data values[0] = 0; values[1] = 0; } else if (is_angle) { values[1] = RAD2DEGF(bezt->vec[0][1]); } else { values[1] = bezt->vec[0][1]; } break; case COLLADASW::InputSemantic::OUT_TANGENT: *length = 2; values[0] = convert_time(bezt->vec[2][0]); if (bezt->ipo != BEZT_IPO_BEZ) { // We're in a mixed interpolation scenario, set zero as it's irrelevant but value might contain unused data values[0] = 0; values[1] = 0; } else if (is_angle) { values[1] = RAD2DEGF(bezt->vec[2][1]); } else { values[1] = bezt->vec[2][1]; } break; default: *length = 0; break; } } // 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) { return create_source_from_fcurve(semantic, fcu, anim_id, axis_name, NULL); } 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); } /* * 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); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(fcu->totvert); switch (semantic) { 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 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[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(); return source_id; } /* * Similar to create_source_from_fcurve, but adds conversion of lens * animation data from focal length to FOV. */ std::string AnimationExporter::create_lens_source_from_fcurve(Camera *cam, COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id) { std::string source_id = anim_id + get_semantic_suffix(semantic); COLLADASW::FloatSourceF source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(fcu->totvert); source.setAccessorStride(1); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, false, "", false); source.prepareToAppendValues(); for (unsigned int i = 0; i < fcu->totvert; i++) { float values[3]; // be careful! int length = 0; get_source_values(&fcu->bezt[i], semantic, false, values, &length); for (int j = 0; j < length; j++) { float val = RAD2DEGF(focallength_to_fov(values[j], cam->sensor_x)); source.appendValues(val); } } source.finish(); return source_id; } /* * 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); COLLADASW::FloatSourceF source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(tot); source.setAccessorStride(1); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, is_rot, axis_name, false); source.prepareToAppendValues(); for (int i = 0; i < tot; i++) { float val = v[i]; ////if (semantic == COLLADASW::InputSemantic::INPUT) // val = convert_time(val); //else if (is_rot) val = RAD2DEGF(val); source.appendValues(val); } source.finish(); return source_id; } /* * 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); COLLADASW::FloatSourceF source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(fra.size()); source.setAccessorStride(1); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, is_rot, axis_name, false); source.prepareToAppendValues(); std::vector::iterator it; for (it = fra.begin(); it != fra.end(); it++) { float val = *it; //if (semantic == COLLADASW::InputSemantic::INPUT) val = convert_time(val); /*else if (is_rot) val = convert_angle(val);*/ source.appendValues(val); } source.finish(); return source_id; } std::string AnimationExporter::create_4x4_source(std::vector &ctimes, std::vector &values , const std::string &anim_id) { COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; std::string source_id = anim_id + get_semantic_suffix(semantic); COLLADASW::Float4x4Source source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(ctimes.size()); source.setAccessorStride(16); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, false, NULL, true); source.prepareToAppendValues(); std::vector::iterator it; for (it = values.begin(); it != values.end(); it+=16) { float mat[4][4]; bc_copy_m4_farray(mat, &*it); UnitConverter converter; double outmat[4][4]; converter.mat4_to_dae_double(outmat, mat); if (this->export_settings->limit_precision) bc_sanitize_mat(outmat, 6); source.appendValues(outmat); } source.finish(); return source_id; } std::string AnimationExporter::create_4x4_source(std::vector &frames, Object *ob, Bone *bone, const std::string &anim_id) { bool is_bone_animation = ob->type == OB_ARMATURE && bone; COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; std::string source_id = anim_id + get_semantic_suffix(semantic); COLLADASW::Float4x4Source source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(frames.size()); source.setAccessorStride(16); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, false, NULL, true); source.prepareToAppendValues(); bPoseChannel *parchan = NULL; bPoseChannel *pchan = NULL; if (is_bone_animation) { bPose *pose = ob->pose; pchan = BKE_pose_channel_find_name(pose, bone->name); if (!pchan) return ""; parchan = pchan->parent; enable_fcurves(ob->adt->action, bone->name); } std::vector::iterator it; 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, frame); bc_update_scene(scene, ctime); if (is_bone_animation) { if (pchan->flag & POSE_CHAIN) { enable_fcurves(ob->adt->action, NULL); BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL); BKE_pose_where_is(scene, ob); } else { BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); } // compute bone local mat if (bone->parent) { invert_m4_m4(ipar, parchan->pose_mat); mul_m4_m4m4(mat, ipar, pchan->pose_mat); } 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 */ if (export_settings->open_sim) { float temp[4][4]; copy_m4_m4(temp, bone->arm_mat); temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; invert_m4(temp); mul_m4_m4m4(mat, mat, temp); if (bone->parent) { copy_m4_m4(temp, bone->parent->arm_mat); temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; mul_m4_m4m4(mat, temp, mat); } } } else { copy_m4_m4(mat, ob->obmat); } UnitConverter converter; double outmat[4][4]; converter.mat4_to_dae_double(outmat, mat); if (this->export_settings->limit_precision) bc_sanitize_mat(outmat, 6); source.appendValues(outmat); j++; BIK_release_tree(scene, ob, ctime); } if (ob->adt) { enable_fcurves(ob->adt->action, NULL); } source.finish(); return source_id; } /* * 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; std::string source_id = anim_id + get_semantic_suffix(semantic); COLLADASW::FloatSourceF source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(tot); source.setAccessorStride(3); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); add_source_parameters(param, semantic, false, NULL, false); source.prepareToAppendValues(); for (int i = 0; i < tot; i++) { source.appendValues(*v, *(v + 1), *(v + 2)); v += 3; } source.finish(); return source_id; } std::string AnimationExporter::create_interpolation_source(FCurve *fcu, const std::string& anim_id, const char *axis_name, bool *has_tangents) { std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); COLLADASW::NameSource source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(fcu->totvert); source.setAccessorStride(1); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); param.push_back("INTERPOLATION"); source.prepareToAppendValues(); *has_tangents = false; for (unsigned int i = 0; i < fcu->totvert; i++) { if (fcu->bezt[i].ipo == BEZT_IPO_BEZ) { source.appendValues(BEZIER_NAME); *has_tangents = true; } else if (fcu->bezt[i].ipo == BEZT_IPO_CONST) { source.appendValues(STEP_NAME); } else { // BEZT_IPO_LIN source.appendValues(LINEAR_NAME); } } // unsupported? -- HERMITE, CARDINAL, BSPLINE, NURBS source.finish(); return source_id; } std::string AnimationExporter::fake_interpolation_source(int tot, const std::string& anim_id, const char *axis_name) { std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); COLLADASW::NameSource source(mSW); source.setId(source_id); source.setArrayId(source_id + ARRAY_ID_SUFFIX); source.setAccessorCount(tot); source.setAccessorStride(1); COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); param.push_back("INTERPOLATION"); source.prepareToAppendValues(); for (int i = 0; i < tot; i++) { source.appendValues(LINEAR_NAME); } source.finish(); return source_id; } std::string AnimationExporter::get_light_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis) { std::string tm_name; // when given rna_path, determine tm_type from it if (rna_path) { char *name = extract_transform_name(rna_path); if (STREQ(name, "color")) tm_type = 1; else if (STREQ(name, "spot_size")) tm_type = 2; else if (STREQ(name, "spot_blend")) tm_type = 3; else if (STREQ(name, "distance")) tm_type = 4; else tm_type = -1; } switch (tm_type) { case 1: tm_name = "color"; break; case 2: tm_name = "fall_off_angle"; break; case 3: tm_name = "fall_off_exponent"; break; case 4: tm_name = "blender/blender_dist"; break; default: tm_name = ""; break; } if (tm_name.size()) { if (axis_name[0]) return tm_name + "." + std::string(axis_name); else return tm_name; } return std::string(""); } std::string AnimationExporter::get_camera_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis) { std::string tm_name; // when given rna_path, determine tm_type from it if (rna_path) { char *name = extract_transform_name(rna_path); if (STREQ(name, "lens")) tm_type = 0; else if (STREQ(name, "ortho_scale")) tm_type = 1; else if (STREQ(name, "clip_end")) tm_type = 2; else if (STREQ(name, "clip_start")) tm_type = 3; else tm_type = -1; } switch (tm_type) { case 0: tm_name = "xfov"; break; case 1: tm_name = "xmag"; break; case 2: tm_name = "zfar"; break; case 3: tm_name = "znear"; break; default: tm_name = ""; break; } if (tm_name.size()) { if (axis_name[0]) return tm_name + "." + std::string(axis_name); else return tm_name; } 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 */ std::string AnimationExporter::get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis) { std::string tm_name; bool is_angle = false; // when given rna_path, determine tm_type from it if (rna_path) { char *name = extract_transform_name(rna_path); if (STREQ(name, "rotation_euler")) tm_type = 0; else if (STREQ(name, "rotation_quaternion")) tm_type = 1; else if (STREQ(name, "scale")) tm_type = 2; else if (STREQ(name, "location")) tm_type = 3; else if (STREQ(name, "specular_hardness")) tm_type = 4; else if (STREQ(name, "specular_color")) tm_type = 5; else if (STREQ(name, "diffuse_color")) tm_type = 6; else if (STREQ(name, "alpha")) tm_type = 7; else if (STREQ(name, "ior")) tm_type = 8; else tm_type = -1; } switch (tm_type) { case 0: case 1: tm_name = "rotation"; is_angle = true; break; case 2: tm_name = "scale"; break; case 3: tm_name = "location"; break; case 4: tm_name = "shininess"; break; case 5: tm_name = "specular"; break; case 6: tm_name = "diffuse"; break; case 7: tm_name = "transparency"; break; case 8: tm_name = "index_of_refraction"; break; default: tm_name = ""; break; } if (tm_name.size()) { if (is_angle) return tm_name + std::string(axis_name) + ".ANGLE"; else if (axis_name[0]) return tm_name + "." + std::string(axis_name); else return tm_name; } return std::string(""); } char *AnimationExporter::extract_transform_name(char *rna_path) { char *dot = strrchr(rna_path, '.'); return dot ? (dot + 1) : rna_path; } /* * enable fcurves driving a specific bone, disable all the rest * if bone_name = NULL enable all fcurves */ void AnimationExporter::enable_fcurves(bAction *act, char *bone_name) { FCurve *fcu; char prefix[200]; if (bone_name) BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { if (bone_name) { if (STREQLEN(fcu->rna_path, prefix, strlen(prefix))) fcu->flag &= ~FCURVE_DISABLED; else fcu->flag |= FCURVE_DISABLED; } else { fcu->flag &= ~FCURVE_DISABLED; } } } bool AnimationExporter::hasAnimations(Scene *sce) { LinkNode *node; for (node=this->export_settings->export_set; node; node=node->next) { Object *ob = (Object *)node->link; FCurve *fcu = 0; //Check for object transform animations if (ob->adt && ob->adt->action) fcu = (FCurve *)ob->adt->action->curves.first; //Check for Lamp parameter animations else if ( (ob->type == OB_LAMP) && ((Lamp *)ob->data)->adt && ((Lamp *)ob->data)->adt->action) fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first); //Check for Camera parameter animations else if ( (ob->type == OB_CAMERA) && ((Camera *)ob->data)->adt && ((Camera *)ob->data)->adt->action) fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first); //Check Material Effect parameter animations. for (int a = 0; a < ob->totcol; a++) { Material *ma = give_current_material(ob, a + 1); if (!ma) continue; if (ma->adt && ma->adt->action) { fcu = (FCurve *)ma->adt->action->curves.first; } } //check shape key animation if (!fcu) { Key *key = BKE_key_from_object(ob); if (key && key->adt && key->adt->action) fcu = (FCurve *)key->adt->action->curves.first; } if (fcu) return true; } return false; } //------------------------------- Not used in the new system.-------------------------------------------------------- void AnimationExporter::find_rotation_frames(Object *ob, std::vector &fra, const char *prefix, int rotmode) { if (rotmode > 0) find_keyframes(ob, fra, prefix, "rotation_euler"); else if (rotmode == ROT_MODE_QUAT) find_keyframes(ob, fra, prefix, "rotation_quaternion"); /*else if (rotmode == ROT_MODE_AXISANGLE) ;*/ } /* 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; for (; fcu; fcu = fcu->next) { if (prefix && !STREQLEN(prefix, fcu->rna_path, strlen(prefix))) continue; char *name = extract_transform_name(fcu->rna_path); if (STREQ(name, tm_name)) { for (unsigned int i = 0; i < fcu->totvert; i++) { float f = fcu->bezt[i].vec[1][0]; if (std::find(fra.begin(), fra.end(), f) == fra.end()) fra.push_back(f); } } } // keep the keys in ascending order std::sort(fra.begin(), fra.end()); } } void AnimationExporter::write_bone_animation(Object *ob_arm, Bone *bone) { if (!ob_arm->adt) return; //write bone animations for 3 transform types //i=0 --> rotations //i=1 --> scale //i=2 --> location for (int i = 0; i < 3; i++) sample_and_write_bone_animation(ob_arm, bone, i); for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) write_bone_animation(ob_arm, child); } void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type) { bArmature *arm = (bArmature *)ob_arm->data; int flag = arm->flag; std::vector fra; char prefix[256]; BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone->name); bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); if (!pchan) return; //Fill frame array with key frame values framed at \param:transform_type switch (transform_type) { case 0: find_rotation_frames(ob_arm, fra, prefix, pchan->rotmode); break; case 1: find_keyframes(ob_arm, fra, prefix, "scale"); break; case 2: find_keyframes(ob_arm, fra, prefix, "location"); break; default: return; } // exit rest position if (flag & ARM_RESTPOS) { arm->flag &= ~ARM_RESTPOS; BKE_pose_where_is(scene, ob_arm); } //v array will hold all values which will be exported. if (fra.size()) { float *values = (float *)MEM_callocN(sizeof(float) * 3 * fra.size(), "temp. anim frames"); sample_animation(values, fra, transform_type, bone, ob_arm, pchan); if (transform_type == 0) { // write x, y, z curves separately if it is rotation float *axisValues = (float *)MEM_callocN(sizeof(float) * fra.size(), "temp. anim frames"); for (int i = 0; i < 3; i++) { for (unsigned int j = 0; j < fra.size(); j++) axisValues[j] = values[j * 3 + i]; dae_bone_animation(fra, axisValues, transform_type, i, id_name(ob_arm), bone->name); } MEM_freeN(axisValues); } else { // write xyz at once if it is location or scale dae_bone_animation(fra, values, transform_type, -1, id_name(ob_arm), bone->name); } MEM_freeN(values); } // restore restpos if (flag & ARM_RESTPOS) arm->flag = flag; BKE_pose_where_is(scene, ob_arm); } void AnimationExporter::sample_animation(float *v, std::vector &frames, int type, Bone *bone, Object *ob_arm, bPoseChannel *pchan) { bPoseChannel *parchan = NULL; bPose *pose = ob_arm->pose; pchan = BKE_pose_channel_find_name(pose, bone->name); if (!pchan) return; parchan = pchan->parent; enable_fcurves(ob_arm->adt->action, bone->name); std::vector::iterator it; for (it = frames.begin(); it != frames.end(); it++) { float mat[4][4], ipar[4][4]; float ctime = BKE_scene_frame_get_from_ctime(scene, *it); BKE_animsys_evaluate_animdata(scene, &ob_arm->id, ob_arm->adt, ctime, ADT_RECALC_ANIM); BKE_pose_where_is_bone(scene, ob_arm, pchan, ctime, 1); // compute bone local mat if (bone->parent) { invert_m4_m4(ipar, parchan->pose_mat); mul_m4_m4m4(mat, ipar, pchan->pose_mat); } else copy_m4_m4(mat, pchan->pose_mat); switch (type) { case 0: mat4_to_eul(v, mat); break; case 1: mat4_to_size(v, mat); break; case 2: copy_v3_v3(v, mat[3]); break; } v += 3; } enable_fcurves(ob_arm->adt->action, NULL); } bool AnimationExporter::validateConstraints(bConstraint *con) { bool valid = true; const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); /* these we can skip completely (invalid constraints...) */ if (cti == NULL) valid = false; if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) valid = false; /* these constraints can't be evaluated anyway */ if (cti->evaluate_constraint == NULL) valid = false; /* influence == 0 should be ignored */ if (con->enforce == 0.0f) valid = false; return valid; }