forked from bartvdbraak/blender
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
This commit is contained in:
parent
4403ca80bd
commit
dd7b9a362d
@ -49,38 +49,210 @@ bool AnimationExporter::exportAnimations(Scene *sce)
|
||||
return has_animations;
|
||||
}
|
||||
|
||||
// called for each exported object
|
||||
|
||||
/*
|
||||
* This function creates a complete LINEAR Collada <Animation> Entry with all needed
|
||||
* <source>, <sampler>, and <channel> entries.
|
||||
* This is is used for creating sampled Transformation Animations for either:
|
||||
*
|
||||
* 1-axis animation:
|
||||
* times contains the time points in seconds from within the timeline
|
||||
* values contains the data (list of single floats)
|
||||
* channel_count = 1
|
||||
* axis_name = ['X' | 'Y' | 'Z']
|
||||
* is_rot indicates if the animation is a rotation
|
||||
*
|
||||
* 3-axis animation:
|
||||
* times contains the time points in seconds from within the timeline
|
||||
* values contains the data (list of floats where each 3 entries are one vector)
|
||||
* channel_count = 3
|
||||
* axis_name = "" (actually not used)
|
||||
* is_rot = false (see xxx below)
|
||||
*
|
||||
* xxx: I tried to create a 3 axis rotation animation
|
||||
* like for translation or scale. But i could not
|
||||
* figure out how to setup the channel for this case.
|
||||
* So for now rotations are exported as 3 separate 1-axis collada animations
|
||||
* See export_sampled_animation() further down.
|
||||
*/
|
||||
void AnimationExporter::create_sampled_animation(int channel_count,
|
||||
std::vector<float> ×,
|
||||
std::vector<float> &values,
|
||||
std::string ob_name,
|
||||
std::string label,
|
||||
std::string axis_name,
|
||||
bool is_rot)
|
||||
{
|
||||
|
||||
char anim_id[200];
|
||||
|
||||
BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), label.c_str(), axis_name.c_str());
|
||||
|
||||
openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING);
|
||||
|
||||
/* create input source */
|
||||
std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, times, false, anim_id, "");
|
||||
|
||||
/* create output source */
|
||||
std::string output_id;
|
||||
if (channel_count == 1)
|
||||
output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, &values[0], values.size(), is_rot, anim_id, axis_name.c_str());
|
||||
else if(channel_count = 3)
|
||||
output_id = create_xyz_source(&values[0], times.size(), anim_id);
|
||||
|
||||
std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
|
||||
COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
|
||||
std::string empty;
|
||||
sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id));
|
||||
sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id));
|
||||
|
||||
/* TODO create in/out tangents source (LINEAR) */
|
||||
std::string interpolation_id = fake_interpolation_source(times.size(), anim_id, "");
|
||||
|
||||
/* Create Sampler */
|
||||
sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
|
||||
addSampler(sampler);
|
||||
|
||||
/* Create channel */
|
||||
std::string target = translate_id(ob_name) + "/" + label + axis_name + ((is_rot) ? ".ANGLE" : "");
|
||||
addChannel(COLLADABU::URI(empty, sampler_id), target);
|
||||
|
||||
closeAnimation();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Export all animation FCurves of an Object.
|
||||
*
|
||||
* Note: This uses the keyframes as sample points,
|
||||
* and exports "baked keyframes" while keeping the tangent infromation
|
||||
* of the FCurves intact. This works for simple cases, but breaks
|
||||
* especially when negative scales are involved in the animation.
|
||||
*
|
||||
* If it is necessary to conserve the Animation precisely then
|
||||
* use export_sampled_animation_set() instead.
|
||||
*/
|
||||
void AnimationExporter::export_keyframed_animation_set(Object *ob)
|
||||
{
|
||||
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
|
||||
|
||||
char *transformName;
|
||||
while (fcu) {
|
||||
//for armature animations as objects
|
||||
if (ob->type == OB_ARMATURE)
|
||||
transformName = fcu->rna_path;
|
||||
else
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
if (
|
||||
STREQ(transformName, "location") ||
|
||||
STREQ(transformName, "scale") ||
|
||||
(STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
|
||||
STREQ(transformName, "rotation_quaternion"))
|
||||
{
|
||||
create_keyframed_animation(ob, fcu, transformName, false);
|
||||
}
|
||||
fcu = fcu->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Export the sampled animation of an Object.
|
||||
*
|
||||
* Note: This steps over all animation frames (step size is given in export_settings.sample_size)
|
||||
* and then evaluates the transformation,
|
||||
* and exports "baked samples" This works always, however currently the interpolation type is set
|
||||
* to LINEAR for now. (maybe later this can be changed to BEZIER)
|
||||
*
|
||||
* Note: If it is necessary to keep the FCurves intact, then use export_keyframed_animation_set() instead.
|
||||
* However be aware that exporting keyframed animation may modify the animation slightly.
|
||||
* Also keyframed animation exports tend to break when negative scales are involved.
|
||||
*/
|
||||
void AnimationExporter::export_sampled_animation_set(Object *ob)
|
||||
{
|
||||
static int LOC = 0;
|
||||
static int EULX = 1;
|
||||
static int EULY = 2;
|
||||
static int EULZ = 3;
|
||||
static int SCALE = 4;
|
||||
static int TIME = 5;
|
||||
|
||||
if (this->export_settings->sampling_rate < 1)
|
||||
return; // to avoid infinite loop
|
||||
|
||||
std::vector<float> baked_curves[6];
|
||||
std::vector<float> &ctimes = baked_curves[TIME];
|
||||
find_sampleframes(ob, ctimes);
|
||||
|
||||
for (std::vector<float>::iterator ctime = ctimes.begin(); ctime != ctimes.end(); ++ctime ) {
|
||||
float fmat[4][4];
|
||||
float floc[3];
|
||||
float fquat[4];
|
||||
float fsize[3];
|
||||
float feul[3];
|
||||
|
||||
evaluate_anim_with_constraints(ob, *ctime); // set object transforms to the frame
|
||||
|
||||
BKE_object_matrix_local_get(ob, fmat);
|
||||
mat4_decompose(floc, fquat, fsize, fmat);
|
||||
quat_to_eul(feul, fquat);
|
||||
|
||||
baked_curves[LOC].push_back(floc[0]);
|
||||
baked_curves[LOC].push_back(floc[1]);
|
||||
baked_curves[LOC].push_back(floc[2]);
|
||||
|
||||
baked_curves[EULX].push_back(feul[0]);
|
||||
baked_curves[EULY].push_back(feul[1]);
|
||||
baked_curves[EULZ].push_back(feul[2]);
|
||||
|
||||
baked_curves[SCALE].push_back(fsize[0]);
|
||||
baked_curves[SCALE].push_back(fsize[1]);
|
||||
baked_curves[SCALE].push_back(fsize[2]);
|
||||
|
||||
}
|
||||
|
||||
std::string ob_name = id_name(ob);
|
||||
|
||||
create_sampled_animation(3, baked_curves[TIME], baked_curves[SCALE], ob_name, "scale", "", false);
|
||||
create_sampled_animation(3, baked_curves[TIME], baked_curves[LOC], ob_name, "location", "", false);
|
||||
|
||||
/* Not sure how to export rotation as a 3channel animation,
|
||||
* so separate into 3 single animations for now:
|
||||
*/
|
||||
|
||||
create_sampled_animation(1, baked_curves[TIME], baked_curves[EULX], ob_name, "rotation", "X", true);
|
||||
create_sampled_animation(1, baked_curves[TIME], baked_curves[EULY], ob_name, "rotation", "Y", true);
|
||||
create_sampled_animation(1, baked_curves[TIME], baked_curves[EULZ], ob_name, "rotation", "Z", true);
|
||||
|
||||
fprintf(stdout, "Animation Export: Baked %zd frames for %s (sampling rate: %d)\n",
|
||||
baked_curves[0].size(),
|
||||
ob->id.name,
|
||||
this->export_settings->sampling_rate);
|
||||
}
|
||||
|
||||
/* called for each exported object */
|
||||
void AnimationExporter::operator()(Object *ob)
|
||||
{
|
||||
FCurve *fcu;
|
||||
char *transformName;
|
||||
|
||||
/* bool isMatAnim = false; */ /* UNUSED */
|
||||
|
||||
//Export transform animations
|
||||
if (ob->adt && ob->adt->action) {
|
||||
fcu = (FCurve *)ob->adt->action->curves.first;
|
||||
|
||||
//transform matrix export for bones are temporarily disabled here.
|
||||
if (ob->type == OB_ARMATURE) {
|
||||
bArmature *arm = (bArmature *)ob->data;
|
||||
for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next)
|
||||
write_bone_animation_matrix(ob, bone);
|
||||
}
|
||||
|
||||
while (fcu) {
|
||||
//for armature animations as objects
|
||||
if (ob->type == OB_ARMATURE)
|
||||
transformName = fcu->rna_path;
|
||||
else
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
if ((STREQ(transformName, "location") || STREQ(transformName, "scale")) ||
|
||||
(STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
|
||||
(STREQ(transformName, "rotation_quaternion")))
|
||||
{
|
||||
dae_animation(ob, fcu, transformName, false);
|
||||
else {
|
||||
if (this->export_settings->sampling_rate == -1) {
|
||||
export_keyframed_animation_set(ob);
|
||||
}
|
||||
else {
|
||||
export_sampled_animation_set(ob);
|
||||
}
|
||||
fcu = fcu->next;
|
||||
}
|
||||
|
||||
}
|
||||
@ -92,14 +264,14 @@ void AnimationExporter::operator()(Object *ob)
|
||||
|
||||
//Export Lamp parameter animations
|
||||
if ( (ob->type == OB_LAMP) && ((Lamp *)ob->data)->adt && ((Lamp *)ob->data)->adt->action) {
|
||||
fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
|
||||
FCurve *fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
|
||||
while (fcu) {
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
if ((STREQ(transformName, "color")) || (STREQ(transformName, "spot_size")) ||
|
||||
(STREQ(transformName, "spot_blend")) || (STREQ(transformName, "distance")))
|
||||
{
|
||||
dae_animation(ob, fcu, transformName, true);
|
||||
create_keyframed_animation(ob, fcu, transformName, true);
|
||||
}
|
||||
fcu = fcu->next;
|
||||
}
|
||||
@ -107,7 +279,7 @@ void AnimationExporter::operator()(Object *ob)
|
||||
|
||||
//Export Camera parameter animations
|
||||
if ( (ob->type == OB_CAMERA) && ((Camera *)ob->data)->adt && ((Camera *)ob->data)->adt->action) {
|
||||
fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first);
|
||||
FCurve *fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first);
|
||||
while (fcu) {
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
@ -116,7 +288,7 @@ void AnimationExporter::operator()(Object *ob)
|
||||
(STREQ(transformName, "clip_end")) ||
|
||||
(STREQ(transformName, "clip_start")))
|
||||
{
|
||||
dae_animation(ob, fcu, transformName, true);
|
||||
create_keyframed_animation(ob, fcu, transformName, true);
|
||||
}
|
||||
fcu = fcu->next;
|
||||
}
|
||||
@ -128,7 +300,7 @@ void AnimationExporter::operator()(Object *ob)
|
||||
if (!ma) continue;
|
||||
if (ma->adt && ma->adt->action) {
|
||||
/* isMatAnim = true; */
|
||||
fcu = (FCurve *)ma->adt->action->curves.first;
|
||||
FCurve *fcu = (FCurve *)ma->adt->action->curves.first;
|
||||
while (fcu) {
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
@ -136,7 +308,7 @@ void AnimationExporter::operator()(Object *ob)
|
||||
(STREQ(transformName, "diffuse_color")) || (STREQ(transformName, "alpha")) ||
|
||||
(STREQ(transformName, "ior")))
|
||||
{
|
||||
dae_animation(ob, fcu, transformName, true, ma);
|
||||
create_keyframed_animation(ob, fcu, transformName, true, ma);
|
||||
}
|
||||
fcu = fcu->next;
|
||||
}
|
||||
@ -167,7 +339,7 @@ void AnimationExporter::export_morph_animation(Object *ob)
|
||||
while (fcu) {
|
||||
transformName = extract_transform_name(fcu->rna_path);
|
||||
|
||||
dae_animation(ob, fcu, transformName, true);
|
||||
create_keyframed_animation(ob, fcu, transformName, true);
|
||||
|
||||
fcu = fcu->next;
|
||||
}
|
||||
@ -200,7 +372,7 @@ void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<fl
|
||||
obtar = ct->tar;
|
||||
|
||||
if (obtar)
|
||||
find_frames(obtar, frames);
|
||||
find_keyframes(obtar, frames);
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets)
|
||||
@ -265,8 +437,8 @@ std::string AnimationExporter::getAnimationPathId(const FCurve *fcu)
|
||||
return translate_id(rna_path);
|
||||
}
|
||||
|
||||
//convert f-curves to animation curves and write
|
||||
void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
|
||||
/* convert f-curves to animation curves and write */
|
||||
void AnimationExporter::create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
|
||||
{
|
||||
const char *axis_name = NULL;
|
||||
char anim_id[200];
|
||||
@ -274,6 +446,8 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
|
||||
bool has_tangents = false;
|
||||
bool quatRotation = false;
|
||||
|
||||
Object *obj = NULL;
|
||||
|
||||
if (STREQ(transformName, "rotation_quaternion") ) {
|
||||
fprintf(stderr, "quaternion rotation curves are not supported. rotation curve will not be exported\n");
|
||||
quatRotation = true;
|
||||
@ -291,15 +465,26 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
|
||||
axis_name = axis_names[fcu->array_index];
|
||||
}
|
||||
|
||||
//axis names for transforms
|
||||
else if (STREQ(transformName, "location") ||
|
||||
STREQ(transformName, "scale") ||
|
||||
STREQ(transformName, "rotation_euler") ||
|
||||
STREQ(transformName, "rotation_quaternion"))
|
||||
/*
|
||||
* Note: Handle transformation animations separately (to apply matrix inverse to fcurves)
|
||||
* We will use the object to evaluate the animation on all keyframes and calculate the
|
||||
* resulting object matrix. We need this to incorporate the
|
||||
* effects of the parent inverse matrix (when it contains a rotation component)
|
||||
*
|
||||
* TODO: try to combine exported fcurves into 3 channel animations like done
|
||||
* in export_sampled_animation(). For now each channel is exported as separate <Animation>.
|
||||
*/
|
||||
|
||||
else if (
|
||||
STREQ(transformName, "scale") ||
|
||||
STREQ(transformName, "location") ||
|
||||
STREQ(transformName, "rotation_euler"))
|
||||
{
|
||||
const char *axis_names[] = {"X", "Y", "Z"};
|
||||
if (fcu->array_index < 3)
|
||||
if (fcu->array_index < 3) {
|
||||
axis_name = axis_names[fcu->array_index];
|
||||
obj = ob;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* no axis name. single parameter */
|
||||
@ -308,7 +493,7 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
|
||||
|
||||
std::string ob_name = std::string("null");
|
||||
|
||||
//Create anim Id
|
||||
/* Create anim Id */
|
||||
if (ob->type == OB_ARMATURE) {
|
||||
ob_name = getObjectBoneName(ob, fcu);
|
||||
BLI_snprintf(
|
||||
@ -357,7 +542,7 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
|
||||
output_id = create_lens_source_from_fcurve((Camera *) ob->data, COLLADASW::InputSemantic::OUTPUT, fcu, anim_id);
|
||||
}
|
||||
else {
|
||||
output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name);
|
||||
output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name, obj);
|
||||
}
|
||||
|
||||
// create interpolations source
|
||||
@ -369,10 +554,10 @@ void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformNa
|
||||
|
||||
if (has_tangents) {
|
||||
// create in_tangent source
|
||||
intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name);
|
||||
intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name, obj);
|
||||
|
||||
// create out_tangent source
|
||||
outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name);
|
||||
outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name, obj);
|
||||
}
|
||||
|
||||
std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
|
||||
@ -475,8 +660,11 @@ void AnimationExporter::sample_and_write_bone_animation_matrix(Object *ob_arm, B
|
||||
if (!pchan)
|
||||
return;
|
||||
|
||||
//every inserted keyframe of bones.
|
||||
find_frames(ob_arm, fra);
|
||||
|
||||
if (this->export_settings->sampling_rate < 1)
|
||||
find_keyframes(ob_arm, fra);
|
||||
else
|
||||
find_sampleframes(ob_arm, fra);
|
||||
|
||||
if (flag & ARM_RESTPOS) {
|
||||
arm->flag &= ~ARM_RESTPOS;
|
||||
@ -755,14 +943,60 @@ void AnimationExporter::get_source_values(BezTriple *bezt, COLLADASW::InputSeman
|
||||
}
|
||||
}
|
||||
|
||||
// old function to keep compatibility for calls where offset and object are not needed
|
||||
std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name)
|
||||
{
|
||||
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 = STREQ(fcu->rna_path, "rotation");
|
||||
bool is_angle = false;
|
||||
|
||||
if (strstr(fcu->rna_path, "rotation") || strstr(fcu->rna_path,"spot_size")) is_angle = true;
|
||||
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<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name)
|
||||
{
|
||||
std::string source_id = anim_id + get_semantic_suffix(semantic);
|
||||
@ -935,9 +1221,10 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
|
||||
int j = 0;
|
||||
for (it = frames.begin(); it != frames.end(); it++) {
|
||||
float mat[4][4], ipar[4][4];
|
||||
float frame = *it;
|
||||
|
||||
float ctime = BKE_scene_frame_get_from_ctime(scene, *it);
|
||||
CFRA = BKE_scene_frame_get_from_ctime(scene, *it);
|
||||
float ctime = BKE_scene_frame_get_from_ctime(scene, frame);
|
||||
CFRA = BKE_scene_frame_get_from_ctime(scene, frame);
|
||||
//BKE_scene_update_for_newframe(G.main->eval_ctx, G.main,scene,scene->lay);
|
||||
BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, ctime, ADT_RECALC_ALL);
|
||||
|
||||
@ -959,9 +1246,10 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
|
||||
else
|
||||
copy_m4_m4(mat, pchan->pose_mat);
|
||||
|
||||
// OPEN_SIM_COMPATIBILITY
|
||||
// AFAIK animation to second life is via BVH, but no
|
||||
// reason to not have the collada-animation be correct
|
||||
/* OPEN_SIM_COMPATIBILITY
|
||||
* AFAIK animation to second life is via BVH, but no
|
||||
* reason to not have the collada-animation be correct
|
||||
*/
|
||||
if (export_settings->open_sim) {
|
||||
float temp[4][4];
|
||||
copy_m4_m4(temp, bone->arm_mat);
|
||||
@ -980,7 +1268,11 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
|
||||
|
||||
}
|
||||
else {
|
||||
calc_ob_mat_at_time(ob, ctime, mat);
|
||||
BKE_scene_frame_set(scene, ctime);
|
||||
Main *bmain = bc_get_main();
|
||||
EvaluationContext *ev_context = bc_get_evaluation_context();
|
||||
BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay);
|
||||
copy_m4_m4(mat, ob->obmat);
|
||||
}
|
||||
|
||||
UnitConverter converter;
|
||||
@ -1008,7 +1300,9 @@ std::string AnimationExporter::create_4x4_source(std::vector<float> &frames, Obj
|
||||
}
|
||||
|
||||
|
||||
// only used for sources with OUTPUT semantic ( locations and scale)
|
||||
/*
|
||||
* only used for sources with OUTPUT semantic ( locations and scale)
|
||||
*/
|
||||
std::string AnimationExporter::create_xyz_source(float *v, int tot, const std::string& anim_id)
|
||||
{
|
||||
COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT;
|
||||
@ -1192,8 +1486,10 @@ std::string AnimationExporter::get_camera_param_sid(char *rna_path, int tm_type,
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
// Assign sid of the animated parameter or transform
|
||||
// for rotation, axis name is always appended and the value of append_axis is ignored
|
||||
/*
|
||||
* Assign sid of the animated parameter or transform for rotation,
|
||||
* axis name is always appended and the value of append_axis is ignored
|
||||
*/
|
||||
std::string AnimationExporter::get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis)
|
||||
{
|
||||
std::string tm_name;
|
||||
@ -1277,29 +1573,10 @@ char *AnimationExporter::extract_transform_name(char *rna_path)
|
||||
return dot ? (dot + 1) : rna_path;
|
||||
}
|
||||
|
||||
//find keyframes of all the objects animations
|
||||
void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra)
|
||||
{
|
||||
if (ob->adt && ob->adt->action) {
|
||||
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
|
||||
|
||||
for (; fcu; fcu = fcu->next) {
|
||||
for (unsigned int i = 0; i < fcu->totvert; i++) {
|
||||
float f = fcu->bezt[i].vec[1][0];
|
||||
if (std::find(fra.begin(), fra.end(), f) == fra.end())
|
||||
fra.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// keep the keys in ascending order
|
||||
std::sort(fra.begin(), fra.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// enable fcurves driving a specific bone, disable all the rest
|
||||
// if bone_name = NULL enable all fcurves
|
||||
/*
|
||||
* enable fcurves driving a specific bone, disable all the rest
|
||||
* if bone_name = NULL enable all fcurves
|
||||
*/
|
||||
void AnimationExporter::enable_fcurves(bAction *act, char *bone_name)
|
||||
{
|
||||
FCurve *fcu;
|
||||
@ -1364,14 +1641,53 @@ bool AnimationExporter::hasAnimations(Scene *sce)
|
||||
void AnimationExporter::find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode)
|
||||
{
|
||||
if (rotmode > 0)
|
||||
find_frames(ob, fra, prefix, "rotation_euler");
|
||||
find_keyframes(ob, fra, prefix, "rotation_euler");
|
||||
else if (rotmode == ROT_MODE_QUAT)
|
||||
find_frames(ob, fra, prefix, "rotation_quaternion");
|
||||
find_keyframes(ob, fra, prefix, "rotation_quaternion");
|
||||
/*else if (rotmode == ROT_MODE_AXISANGLE)
|
||||
;*/
|
||||
}
|
||||
|
||||
void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name)
|
||||
/* Take care to always have the first frame and the last frame in the animation
|
||||
* regardless of the sampling_rate setting
|
||||
*/
|
||||
void AnimationExporter::find_sampleframes(Object *ob, std::vector<float> &fra)
|
||||
{
|
||||
int frame = scene->r.sfra;
|
||||
do {
|
||||
float ctime = BKE_scene_frame_get_from_ctime(scene, frame);
|
||||
fra.push_back(ctime);
|
||||
if (frame == scene->r.efra)
|
||||
break;
|
||||
frame += this->export_settings->sampling_rate;
|
||||
if (frame > scene->r.efra)
|
||||
frame = scene->r.efra; // make sure the last frame is always exported
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* find keyframes of all the objects animations
|
||||
*/
|
||||
void AnimationExporter::find_keyframes(Object *ob, std::vector<float> &fra)
|
||||
{
|
||||
if (ob->adt && ob->adt->action) {
|
||||
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
|
||||
|
||||
for (; fcu; fcu = fcu->next) {
|
||||
for (unsigned int i = 0; i < fcu->totvert; i++) {
|
||||
float f = fcu->bezt[i].vec[1][0];
|
||||
if (std::find(fra.begin(), fra.end(), f) == fra.end())
|
||||
fra.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
// keep the keys in ascending order
|
||||
std::sort(fra.begin(), fra.end());
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationExporter::find_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name)
|
||||
{
|
||||
if (ob->adt && ob->adt->action) {
|
||||
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
|
||||
@ -1429,10 +1745,10 @@ void AnimationExporter::sample_and_write_bone_animation(Object *ob_arm, Bone *bo
|
||||
find_rotation_frames(ob_arm, fra, prefix, pchan->rotmode);
|
||||
break;
|
||||
case 1:
|
||||
find_frames(ob_arm, fra, prefix, "scale");
|
||||
find_keyframes(ob_arm, fra, prefix, "scale");
|
||||
break;
|
||||
case 2:
|
||||
find_frames(ob_arm, fra, prefix, "location");
|
||||
find_keyframes(ob_arm, fra, prefix, "location");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@ -1539,8 +1855,23 @@ bool AnimationExporter::validateConstraints(bConstraint *con)
|
||||
return valid;
|
||||
}
|
||||
|
||||
void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[][4])
|
||||
#if 0
|
||||
/*
|
||||
* Needed for sampled animations.
|
||||
* This function calculates the object matrix at a given time,
|
||||
* also taking constraints into account.
|
||||
*
|
||||
* XXX: Why looking at the constraints here is necessary?
|
||||
* Maybe this can be done better?
|
||||
*/
|
||||
void AnimationExporter::calc_obmat_at_time(Object *ob, float ctime )
|
||||
{
|
||||
BKE_scene_frame_set(scene, ctime);
|
||||
|
||||
Main *bmain = bc_get_main();
|
||||
EvaluationContext *ev_context = bc_get_evaluation_context();
|
||||
BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay);
|
||||
|
||||
ListBase *conlist = get_active_constraints(ob);
|
||||
bConstraint *con;
|
||||
for (con = (bConstraint *)conlist->first; con; con = con->next) {
|
||||
@ -1566,6 +1897,6 @@ void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[
|
||||
}
|
||||
}
|
||||
BKE_object_where_is_calc_time(scene, ob, ctime);
|
||||
copy_m4_m4(mat, ob->obmat);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -90,9 +90,11 @@ private:
|
||||
public:
|
||||
|
||||
AnimationExporter(COLLADASW::StreamWriter *sw, const ExportSettings *export_settings):
|
||||
COLLADASW::LibraryAnimations(sw), export_settings(export_settings)
|
||||
{ this->sw = sw; }
|
||||
|
||||
COLLADASW::LibraryAnimations(sw),
|
||||
export_settings(export_settings)
|
||||
{
|
||||
this->sw = sw;
|
||||
}
|
||||
|
||||
bool exportAnimations(Scene *sce);
|
||||
|
||||
@ -102,7 +104,6 @@ public:
|
||||
protected:
|
||||
const ExportSettings *export_settings;
|
||||
|
||||
void dae_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma = NULL);
|
||||
|
||||
void export_object_constraint_animation(Object *ob);
|
||||
|
||||
@ -143,7 +144,15 @@ protected:
|
||||
|
||||
float* get_eul_source_for_quat(Object *ob );
|
||||
|
||||
void export_keyframed_animation_set(Object *ob);
|
||||
void create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma = NULL);
|
||||
void export_sampled_animation_set(Object *ob);
|
||||
void create_sampled_animation(int channel_count, std::vector<float> ×, std::vector<float> &values, std::string, std::string label, std::string axis_name, bool is_rot);
|
||||
|
||||
void evaluate_anim_with_constraints(Object *ob, float ctime);
|
||||
|
||||
std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name);
|
||||
std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name, Object *ob);
|
||||
|
||||
std::string create_lens_source_from_fcurve(Camera *cam, COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id);
|
||||
|
||||
@ -164,8 +173,10 @@ protected:
|
||||
std::string get_light_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
|
||||
std::string get_camera_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
|
||||
|
||||
void find_frames(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name);
|
||||
void find_frames(Object *ob, std::vector<float> &fra);
|
||||
void find_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name);
|
||||
void find_keyframes(Object *ob, std::vector<float> &fra);
|
||||
void find_sampleframes(Object *ob, std::vector<float> &fra);
|
||||
|
||||
|
||||
void make_anim_frames_from_targets(Object *ob, std::vector<float> &frames );
|
||||
|
||||
@ -186,6 +197,6 @@ protected:
|
||||
|
||||
bool validateConstraints(bConstraint *con);
|
||||
|
||||
void calc_ob_mat_at_time(Object *ob, float ctime , float mat[][4]);
|
||||
//void calc_obmat_at_time(Object *ob, float ctime);
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
bool include_armatures;
|
||||
bool include_shapekeys;
|
||||
bool deform_bones_only;
|
||||
int sampling_rate;
|
||||
|
||||
bool active_uv_only;
|
||||
BC_export_texture_type export_texture_type;
|
||||
|
@ -78,6 +78,7 @@ int collada_export(Scene *sce,
|
||||
int include_armatures,
|
||||
int include_shapekeys,
|
||||
int deform_bones_only,
|
||||
int sampling_rate,
|
||||
|
||||
int active_uv_only,
|
||||
BC_export_texture_type export_texture_type,
|
||||
@ -103,6 +104,7 @@ int collada_export(Scene *sce,
|
||||
export_settings.include_armatures = include_armatures != 0;
|
||||
export_settings.include_shapekeys = include_shapekeys != 0;
|
||||
export_settings.deform_bones_only = deform_bones_only != 0;
|
||||
export_settings.sampling_rate = sampling_rate;
|
||||
|
||||
export_settings.active_uv_only = active_uv_only != 0;
|
||||
export_settings.export_texture_type = export_texture_type;
|
||||
|
@ -77,6 +77,7 @@ int collada_export(struct Scene *sce,
|
||||
int include_armatures,
|
||||
int include_shapekeys,
|
||||
int deform_bones_only,
|
||||
int sampling_rate,
|
||||
|
||||
int active_uv_only,
|
||||
BC_export_texture_type export_texture_type,
|
||||
|
@ -53,6 +53,7 @@ extern "C" {
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "ED_armature.h"
|
||||
|
||||
@ -132,6 +133,17 @@ int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space)
|
||||
return true;
|
||||
}
|
||||
|
||||
Main *bc_get_main()
|
||||
{
|
||||
return G.main;
|
||||
}
|
||||
|
||||
EvaluationContext *bc_get_evaluation_context()
|
||||
{
|
||||
Main *bmain = G.main;
|
||||
return bmain->eval_ctx;
|
||||
}
|
||||
|
||||
Object *bc_add_object(Scene *scene, int type, const char *name)
|
||||
{
|
||||
Object *ob = BKE_object_add_only_object(G.main, type, name);
|
||||
|
@ -63,6 +63,9 @@ extern "C" {
|
||||
|
||||
typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex *> > TexIndexTextureArrayMap;
|
||||
|
||||
extern Main *bc_get_main();
|
||||
extern EvaluationContext *bc_get_evaluation_context();
|
||||
|
||||
extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index);
|
||||
extern int bc_test_parent_loop(Object *par, Object *ob);
|
||||
extern int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true);
|
||||
|
@ -86,6 +86,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
|
||||
int include_armatures;
|
||||
int include_shapekeys;
|
||||
int deform_bones_only;
|
||||
int sampling_rate;
|
||||
|
||||
int export_texture_type;
|
||||
int use_texture_copies;
|
||||
@ -136,6 +137,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
|
||||
include_children = RNA_boolean_get(op->ptr, "include_children");
|
||||
include_armatures = RNA_boolean_get(op->ptr, "include_armatures");
|
||||
include_shapekeys = RNA_boolean_get(op->ptr, "include_shapekeys");
|
||||
sampling_rate = RNA_int_get(op->ptr, "sampling_rate");
|
||||
deform_bones_only = RNA_boolean_get(op->ptr, "deform_bones_only");
|
||||
|
||||
export_texture_type = RNA_enum_get(op->ptr, "export_texture_type_selection");
|
||||
@ -165,6 +167,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
|
||||
include_armatures,
|
||||
include_shapekeys,
|
||||
deform_bones_only,
|
||||
sampling_rate,
|
||||
|
||||
active_uv_only,
|
||||
export_texture_type,
|
||||
@ -229,6 +232,10 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
|
||||
uiItemR(row, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
|
||||
uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
|
||||
|
||||
row = uiLayoutRow(box, false);
|
||||
uiItemR(row, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
|
||||
|
||||
|
||||
/* Texture options */
|
||||
box = uiLayoutBox(layout);
|
||||
row = uiLayoutRow(box, false);
|
||||
@ -251,6 +258,7 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
|
||||
|
||||
row = uiLayoutRow(box, false);
|
||||
uiItemR(row, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
|
||||
|
||||
row = uiLayoutRow(box, false);
|
||||
uiItemR(row, imfptr, "open_sim", 0, NULL, ICON_NONE);
|
||||
|
||||
@ -366,7 +374,10 @@ void WM_OT_collada_export(wmOperatorType *ot)
|
||||
"Export all Shape Keys from Mesh Objects");
|
||||
|
||||
RNA_def_boolean(func, "deform_bones_only", 0, "Deform Bones only",
|
||||
"Only export deforming bones with armatures");
|
||||
"Only export deforming bones with armatures");
|
||||
|
||||
RNA_def_int(func, "sampling_rate", 0, -1, INT_MAX,
|
||||
"Samplintg Rate", "The maximum distance of frames between 2 keyframes. Disabled when value is -1", -1, INT_MAX);
|
||||
|
||||
RNA_def_boolean(func, "active_uv_only", 0, "Only Selected UV Map",
|
||||
"Export only the selected UV Map");
|
||||
|
@ -278,6 +278,7 @@ static void rna_Scene_collada_export(
|
||||
int include_armatures,
|
||||
int include_shapekeys,
|
||||
int deform_bones_only,
|
||||
int sampling_rate,
|
||||
int active_uv_only,
|
||||
int export_texture_type,
|
||||
int use_texture_copies,
|
||||
@ -301,6 +302,7 @@ static void rna_Scene_collada_export(
|
||||
include_armatures,
|
||||
include_shapekeys,
|
||||
deform_bones_only,
|
||||
sampling_rate,
|
||||
|
||||
active_uv_only,
|
||||
export_texture_type,
|
||||
@ -400,6 +402,9 @@ void RNA_api_scene(StructRNA *srna)
|
||||
RNA_def_boolean(func, "deform_bones_only", false,
|
||||
"Deform Bones only", "Only export deforming bones with armatures");
|
||||
|
||||
RNA_def_int(func, "sampling_rate", 0, -1, INT_MAX,
|
||||
"Samplintg Rate", "The maximum distance of frames between 2 keyframes. Disabled when value is -1", -1, INT_MAX);
|
||||
|
||||
RNA_def_boolean(func, "active_uv_only", false, "Only Selected UV Map", "Export only the selected UV Map");
|
||||
|
||||
RNA_def_int(func, "export_texture_type", 0, INT_MIN, INT_MAX,
|
||||
|
@ -616,3 +616,5 @@ if(WITH_ALEMBIC)
|
||||
--with-legacy-depsgraph=${WITH_LEGACY_DEPSGRAPH}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(collada)
|
||||
|
65
tests/python/collada/CMakeLists.txt
Normal file
65
tests/python/collada/CMakeLists.txt
Normal file
@ -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)
|
144
tests/python/collada/mesh/test_mesh_simple.py
Normal file
144
tests/python/collada/mesh/test_mesh_simple.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user