Summary:
Made objects update happening from multiple threads. It is a task-based
scheduling system which uses current dependency graph for spawning new
tasks. This means threading happens on object level, but the system is
flexible enough for higher granularity.
Technical details:
- Uses task scheduler which was recently committed to trunk
(that one which Brecht ported from Cycles).
- Added two utility functions to dependency graph:
* DAG_threaded_update_begin, which is called to initialize threaded
objects update. It will also schedule root DAG node to the queue,
hence starting evaluation process.
Initialization will calculate how much parents are to be evaluation
before current DAG node can be scheduled. This value is used by task
threads for faster detecting which nodes might be scheduled.
* DAG_threaded_update_handle_node_updated which is called from task
thread function when node was fully handled.
This function decreases num_pending_parents of node children and
schedules children with zero valency.
As it might have become clear, task thread receives DAG nodes and
decides which callback to call for it.
Currently only BKE_object_handle_update is called for object nodes.
In the future it'll call node->callback() from Ali's new DAG.
- This required adding some workarounds to the render pipeline.
Mainly to stop using get_object_dm() from modifiers' apply callback.
Such a call was only a workaround for dependency graph glitch when
rendering scene with, say, boolean modifiers before displaying
this scene.
Such change moves workaround from one place to another, so overall
hackentropy remains the same.
- Added paradigm of EvaluaitonContext. Currently it's more like just a
more reliable replacement for G.is_rendering which fails in some
circumstances.
Future idea of this context is to also store all the local data needed
for objects evaluation such as local time, Copy-on-Write data and so.
There're two types of EvaluationContext:
* Context used for viewport updated and owned by Main. In the future
this context might be easily moved to Window or Screen to allo
per-window/per-screen local time.
* Context used by render engines to evaluate objects for render purposes.
Render engine is an owner of this context.
This context is passed to all object update routines.
Reviewers: brecht, campbellbarton
Reviewed By: brecht
CC: lukastoenne
Differential Revision: https://developer.blender.org/D94
1568 lines
43 KiB
C++
1568 lines
43 KiB
C++
/*
|
|
* ***** 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"
|
|
|
|
Global G;
|
|
|
|
template<class Functor>
|
|
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;
|
|
}
|
|
|
|
// 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 ((!strcmp(transformName, "location") || !strcmp(transformName, "scale")) ||
|
|
(!strcmp(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
|
|
(!strcmp(transformName, "rotation_quaternion")))
|
|
{
|
|
dae_animation(ob, fcu, transformName, false);
|
|
}
|
|
fcu = fcu->next;
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
|
fcu = (FCurve *)(((Lamp *)ob->data)->adt->action->curves.first);
|
|
while (fcu) {
|
|
transformName = extract_transform_name(fcu->rna_path);
|
|
|
|
if ((!strcmp(transformName, "color")) || (!strcmp(transformName, "spot_size")) ||
|
|
(!strcmp(transformName, "spot_blend")) || (!strcmp(transformName, "distance")))
|
|
{
|
|
dae_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) {
|
|
fcu = (FCurve *)(((Camera *)ob->data)->adt->action->curves.first);
|
|
while (fcu) {
|
|
transformName = extract_transform_name(fcu->rna_path);
|
|
|
|
if ((!strcmp(transformName, "lens")) ||
|
|
(!strcmp(transformName, "ortho_scale")) ||
|
|
(!strcmp(transformName, "clip_end")) ||
|
|
(!strcmp(transformName, "clip_start")))
|
|
{
|
|
dae_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; */
|
|
fcu = (FCurve *)ma->adt->action->curves.first;
|
|
while (fcu) {
|
|
transformName = extract_transform_name(fcu->rna_path);
|
|
|
|
if ((!strcmp(transformName, "specular_hardness")) || (!strcmp(transformName, "specular_color")) ||
|
|
(!strcmp(transformName, "diffuse_color")) || (!strcmp(transformName, "alpha")) ||
|
|
(!strcmp(transformName, "ior")))
|
|
{
|
|
dae_animation(ob, fcu, transformName, true, ma);
|
|
}
|
|
fcu = fcu->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void AnimationExporter::export_object_constraint_animation(Object *ob)
|
|
{
|
|
std::vector<float> 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);
|
|
|
|
dae_animation(ob, fcu, transformName, true);
|
|
|
|
fcu = fcu->next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<float> &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};
|
|
|
|
bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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_frames(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 (!strcmp(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::dae_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;
|
|
|
|
if (!strcmp(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 (!strcmp(transformName, "color") ||
|
|
!strcmp(transformName, "specular_color") ||
|
|
!strcmp(transformName, "diffuse_color") ||
|
|
!strcmp(transformName, "alpha"))
|
|
{
|
|
const char *axis_names[] = {"R", "G", "B"};
|
|
if (fcu->array_index < 3)
|
|
axis_name = axis_names[fcu->array_index];
|
|
}
|
|
|
|
//axis names for transforms
|
|
else if (!strcmp(transformName, "location") ||
|
|
!strcmp(transformName, "scale") ||
|
|
!strcmp(transformName, "rotation_euler") ||
|
|
!strcmp(transformName, "rotation_quaternion"))
|
|
{
|
|
const char *axis_names[] = {"X", "Y", "Z"};
|
|
if (fcu->array_index < 3)
|
|
axis_name = axis_names[fcu->array_index];
|
|
}
|
|
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 (!strcmp(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);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// create out_tangent source
|
|
outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, 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));
|
|
|
|
// 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<float> 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;
|
|
|
|
//every inserted keyframe of bones.
|
|
find_frames(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<float> &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 = translate_id(bone_name) + "/transform";
|
|
addChannel(COLLADABU::URI(empty, sampler_id), target);
|
|
|
|
closeAnimation();
|
|
}
|
|
|
|
void AnimationExporter::dae_baked_object_animation(std::vector<float> &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<float> &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_rotation, 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_rotation) {
|
|
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_rotation) {
|
|
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_rotation) {
|
|
values[1] = RAD2DEGF(bezt->vec[2][1]);
|
|
}
|
|
else {
|
|
values[1] = bezt->vec[2][1];
|
|
}
|
|
break;
|
|
default:
|
|
*length = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::string AnimationExporter::create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name)
|
|
{
|
|
std::string source_id = anim_id + get_semantic_suffix(semantic);
|
|
|
|
//bool is_rotation = !strcmp(fcu->rna_path, "rotation");
|
|
bool is_angle = false;
|
|
|
|
if (strstr(fcu->rna_path, "rotation")) is_angle = true;
|
|
|
|
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);
|
|
break;
|
|
case COLLADASW::InputSemantic::IN_TANGENT:
|
|
case COLLADASW::InputSemantic::OUT_TANGENT:
|
|
source.setAccessorStride(2);
|
|
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++) {
|
|
float values[3]; // be careful!
|
|
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]);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
//Currently called 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<float> &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<float>::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<float> &frames, Object *ob, Bone *bone, 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(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 (ob->type == OB_ARMATURE && bone) {
|
|
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<float>::iterator it;
|
|
int j = 0;
|
|
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);
|
|
CFRA = BKE_scene_frame_get_from_ctime(scene, *it);
|
|
//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);
|
|
|
|
if (bone) {
|
|
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 {
|
|
calc_ob_mat_at_time(ob, ctime, mat);
|
|
}
|
|
|
|
UnitConverter converter;
|
|
|
|
double outmat[4][4];
|
|
converter.mat4_to_dae_double(outmat, mat);
|
|
|
|
source.appendValues(outmat);
|
|
|
|
j++;
|
|
|
|
BIK_release_tree(scene, ob, ctime);
|
|
}
|
|
|
|
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 (!strcmp(name, "color"))
|
|
tm_type = 1;
|
|
else if (!strcmp(name, "spot_size"))
|
|
tm_type = 2;
|
|
else if (!strcmp(name, "spot_blend"))
|
|
tm_type = 3;
|
|
else if (!strcmp(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 (!strcmp(name, "lens"))
|
|
tm_type = 0;
|
|
else if (!strcmp(name, "ortho_scale"))
|
|
tm_type = 1;
|
|
else if (!strcmp(name, "clip_end"))
|
|
tm_type = 2;
|
|
else if (!strcmp(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_rotation = false;
|
|
// when given rna_path, determine tm_type from it
|
|
if (rna_path) {
|
|
char *name = extract_transform_name(rna_path);
|
|
|
|
if (!strcmp(name, "rotation_euler"))
|
|
tm_type = 0;
|
|
else if (!strcmp(name, "rotation_quaternion"))
|
|
tm_type = 1;
|
|
else if (!strcmp(name, "scale"))
|
|
tm_type = 2;
|
|
else if (!strcmp(name, "location"))
|
|
tm_type = 3;
|
|
else if (!strcmp(name, "specular_hardness"))
|
|
tm_type = 4;
|
|
else if (!strcmp(name, "specular_color"))
|
|
tm_type = 5;
|
|
else if (!strcmp(name, "diffuse_color"))
|
|
tm_type = 6;
|
|
else if (!strcmp(name, "alpha"))
|
|
tm_type = 7;
|
|
else if (!strcmp(name, "ior"))
|
|
tm_type = 8;
|
|
|
|
else
|
|
tm_type = -1;
|
|
}
|
|
|
|
switch (tm_type) {
|
|
case 0:
|
|
case 1:
|
|
tm_name = "rotation";
|
|
is_rotation = 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_rotation)
|
|
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;
|
|
}
|
|
|
|
//find keyframes of all the objects animations
|
|
void AnimationExporter::find_frames(Object *ob, std::vector<float> &fra)
|
|
{
|
|
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
|
|
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 (!strncmp(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<float> &fra, const char *prefix, int rotmode)
|
|
{
|
|
if (rotmode > 0)
|
|
find_frames(ob, fra, prefix, "rotation_euler");
|
|
else if (rotmode == ROT_MODE_QUAT)
|
|
find_frames(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)
|
|
{
|
|
FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
|
|
|
|
for (; fcu; fcu = fcu->next) {
|
|
if (prefix && strncmp(prefix, fcu->rna_path, strlen(prefix)))
|
|
continue;
|
|
|
|
char *name = extract_transform_name(fcu->rna_path);
|
|
if (!strcmp(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<float> 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_frames(ob_arm, fra, prefix, "scale");
|
|
break;
|
|
case 2:
|
|
find_frames(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<float> &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<float>::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;
|
|
bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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;
|
|
}
|
|
|
|
void AnimationExporter::calc_ob_mat_at_time(Object *ob, float ctime , float mat[][4])
|
|
{
|
|
ListBase *conlist = get_active_constraints(ob);
|
|
bConstraint *con;
|
|
for (con = (bConstraint *)conlist->first; con; con = con->next) {
|
|
ListBase targets = {NULL, NULL};
|
|
|
|
bConstraintTypeInfo *cti = BKE_constraint_get_typeinfo(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);
|
|
copy_m4_m4(mat, ob->obmat);
|
|
}
|
|
|