* added armature/skinned mesh export

* meshes are now exported without modifiers applied (for skinning to work), this should become a configurable option later
* had to edit OpenCollada code to make armature/skinning import work correctly
* code layout improvements
This commit is contained in:
Chingiz Dyussenov
2009-08-03 14:40:23 +00:00
parent 464735402e
commit 87e68418a2
3 changed files with 1109 additions and 710 deletions

View File

@@ -27,6 +27,7 @@ extern "C"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_action.h" // pose functions
#include "BKE_armature.h"
#include "BLI_arithb.h"
#include "BLI_string.h"
@@ -64,6 +65,7 @@ extern "C"
#include "COLLADASWCameraOptic.h"
#include "COLLADASWConstants.h"
#include "COLLADASWLibraryControllers.h"
#include "COLLADASWInstanceController.h"
#include "COLLADASWBaseInputElement.h"
#include "collada_internal.h"
@@ -114,11 +116,27 @@ char *CustomData_get_active_layer_name(const CustomData *data, int type)
return data->layers[layer_index].name;
}
std::string id_name(void *id)
static std::string id_name(void *id)
{
return ((ID*)id)->name + 2;
}
static std::string get_geometry_id(Object *ob)
{
return id_name(ob) + "-mesh";
}
static void replace_chars(char *str, char chars[], char with)
{
char *ch, *p;
for (ch = chars; *ch; ch++) {
while ((p = strchr(str, *ch))) {
*p = with;
}
}
}
/*
Utilities to avoid code duplication.
Definition can take some time to understand, but they should be useful.
@@ -251,32 +269,33 @@ public:
void operator()(Object *ob)
{
// XXX don't use DerivedMesh, Mesh instead?
#if 0
DerivedMesh *dm = mesh_get_derived_final(mScene, ob, CD_MASK_BAREMESH);
#endif
Mesh *me = (Mesh*)ob->data;
std::string geom_name(id_name(ob));
std::string geom_id = get_geometry_id(ob);
// openMesh(geoId, geoName, meshId)
openMesh(geom_name, "", "");
openMesh(geom_id, "", "");
// writes <source> for vertex coords
createVertsSource(geom_name, dm);
createVertsSource(geom_id, me);
// writes <source> for normal coords
createNormalsSource(geom_name, dm);
createNormalsSource(geom_id, me);
int has_uvs = CustomData_has_layer(&me->fdata, CD_MTFACE);
// writes <source> for uv coords if mesh has uv coords
if (has_uvs) {
createTexcoordsSource(geom_name, dm, (Mesh*)ob->data);
createTexcoordsSource(geom_id, (Mesh*)ob->data);
}
// <vertices>
COLLADASW::Vertices verts(mSW);
verts.setId(getIdBySemantics(geom_name, COLLADASW::VERTEX));
verts.setId(getIdBySemantics(geom_id, COLLADASW::VERTEX));
COLLADASW::InputList &input_list = verts.getInputList();
COLLADASW::Input input(COLLADASW::POSITION,
getUrlBySemantics(geom_name, COLLADASW::POSITION));
COLLADASW::Input input(COLLADASW::POSITION, getUrlBySemantics(geom_id, COLLADASW::POSITION));
input_list.push_back(input);
verts.add();
@@ -285,18 +304,19 @@ public:
for(int a = 0; a < ob->totcol; a++) {
// account for NULL materials, this should not normally happen?
Material *ma = give_current_material(ob, a + 1);
createPolylist(ma != NULL, a, has_uvs, ob, dm, geom_name);
createPolylist(ma != NULL, a, has_uvs, ob, geom_id);
}
}
else {
createPolylist(false, 0, has_uvs, ob, dm, geom_name);
createPolylist(false, 0, has_uvs, ob, geom_id);
}
closeMesh();
closeGeometry();
#if 0
dm->release(dm);
#endif
}
// powerful because it handles both cases when there is material and when there's not
@@ -304,12 +324,15 @@ public:
int material_index,
bool has_uvs,
Object *ob,
DerivedMesh *dm,
std::string& geom_name)
std::string& geom_id)
{
#if 0
MFace *mfaces = dm->getFaceArray(dm);
int totfaces = dm->getNumFaces(dm);
#endif
Mesh *me = (Mesh*)ob->data;
MFace *mfaces = me->mface;
int totfaces = me->totface;
// <vcount>
int i;
@@ -350,11 +373,11 @@ public:
// creates <input> in <polylist> for vertices
COLLADASW::Input input1(COLLADASW::VERTEX, getUrlBySemantics
(geom_name, COLLADASW::VERTEX), 0);
(geom_id, COLLADASW::VERTEX), 0);
// creates <input> in <polylist> for normals
COLLADASW::Input input2(COLLADASW::NORMAL, getUrlBySemantics
(geom_name, COLLADASW::NORMAL), 0);
(geom_id, COLLADASW::NORMAL), 0);
til.push_back(input1);
til.push_back(input2);
@@ -365,7 +388,7 @@ public:
for (i = 0; i < num_layers; i++) {
char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
COLLADASW::Input input3(COLLADASW::TEXCOORD,
makeUrl(makeTexcoordSourceId(geom_name, i)),
makeUrl(makeTexcoordSourceId(geom_id, i)),
1, // offset always 1, this is only until we have optimized UV sets
i // set number equals UV layer index
);
@@ -403,15 +426,18 @@ public:
}
// creates <source> for positions
void createVertsSource(std::string geom_name, DerivedMesh *dm)
void createVertsSource(std::string geom_id, Mesh *me)
{
#if 0
int totverts = dm->getNumVerts(dm);
MVert *verts = dm->getVertArray(dm);
#endif
int totverts = me->totvert;
MVert *verts = me->mvert;
COLLADASW::FloatSourceF source(mSW);
source.setId(getIdBySemantics(geom_name, COLLADASW::POSITION));
source.setArrayId(getIdBySemantics(geom_name, COLLADASW::POSITION) +
source.setId(getIdBySemantics(geom_id, COLLADASW::POSITION));
source.setArrayId(getIdBySemantics(geom_id, COLLADASW::POSITION) +
ARRAY_ID_SUFFIX);
source.setAccessorCount(totverts);
source.setAccessorStride(3);
@@ -425,27 +451,30 @@ public:
//appends data to <float_array>
int i = 0;
for (i = 0; i < totverts; i++) {
source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);
source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);
}
source.finish();
}
std::string makeTexcoordSourceId(std::string& geom_name, int layer_index)
std::string makeTexcoordSourceId(std::string& geom_id, int layer_index)
{
char suffix[20];
sprintf(suffix, "-%d", layer_index);
return getIdBySemantics(geom_name, COLLADASW::TEXCOORD) + suffix;
return getIdBySemantics(geom_id, COLLADASW::TEXCOORD) + suffix;
}
//creates <source> for texcoords
void createTexcoordsSource(std::string geom_name, DerivedMesh *dm, Mesh *me)
void createTexcoordsSource(std::string geom_id, Mesh *me)
{
#if 0
int totfaces = dm->getNumFaces(dm);
MFace *mfaces = dm->getFaceArray(dm);
#endif
int totfaces = me->totface;
MFace *mfaces = me->mface;
int totuv = 0;
int i;
@@ -469,7 +498,7 @@ public:
char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a);
COLLADASW::FloatSourceF source(mSW);
std::string layer_id = makeTexcoordSourceId(geom_name, a);
std::string layer_id = makeTexcoordSourceId(geom_id, a);
source.setId(layer_id);
source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
@@ -496,14 +525,19 @@ public:
//creates <source> for normals
void createNormalsSource(std::string geom_name, DerivedMesh *dm)
void createNormalsSource(std::string geom_id, Mesh *me)
{
#if 0
int totverts = dm->getNumVerts(dm);
MVert *verts = dm->getVertArray(dm);
#endif
int totverts = me->totvert;
MVert *verts = me->mvert;
COLLADASW::FloatSourceF source(mSW);
source.setId(getIdBySemantics(geom_name, COLLADASW::NORMAL));
source.setArrayId(getIdBySemantics(geom_name, COLLADASW::NORMAL) +
source.setId(getIdBySemantics(geom_id, COLLADASW::NORMAL));
source.setArrayId(getIdBySemantics(geom_id, COLLADASW::NORMAL) +
ARRAY_ID_SUFFIX);
source.setAccessorCount(totverts);
source.setAccessorStride(3);
@@ -526,14 +560,14 @@ public:
source.finish();
}
std::string getIdBySemantics(std::string geom_name, COLLADASW::Semantics type, std::string other_suffix = "") {
return geom_name + getSuffixBySemantic(type) + other_suffix;
std::string getIdBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
return geom_id + getSuffixBySemantic(type) + other_suffix;
}
COLLADASW::URI getUrlBySemantics(std::string geom_name, COLLADASW::Semantics type, std::string other_suffix = "") {
COLLADASW::URI getUrlBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
std::string id(getIdBySemantics(geom_name, type, other_suffix));
std::string id(getIdBySemantics(geom_id, type, other_suffix));
return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
}
@@ -559,14 +593,120 @@ public:
}*/
};
// XXX exporter assumes armatures are not shared between meshes.
class ArmatureExporter: COLLADASW::LibraryControllers
class TransformWriter : protected TransformBase
{
protected:
void add_node_transform(COLLADASW::Node& node, float mat[][4])
{
float loc[3], rot[3], size[3];
TransformBase::decompose(mat, loc, rot, size);
/*
// this code used to create a single <rotate> representing object rotation
float quat[4];
float axis[3];
float angle;
double angle_deg;
EulToQuat(rot, quat);
NormalQuat(quat);
QuatToAxisAngle(quat, axis, &angle);
angle_deg = angle * 180.0f / M_PI;
node.addRotate(axis[0], axis[1], axis[2], angle_deg);
*/
node.addTranslate("location", loc[0], loc[1], loc[2]);
node.addRotateZ("rotationZ", COLLADABU::Math::Utils::radToDegF(rot[2]));
node.addRotateY("rotationY", COLLADABU::Math::Utils::radToDegF(rot[1]));
node.addRotateX("rotationX", COLLADABU::Math::Utils::radToDegF(rot[0]));
node.addScale("scale", size[0], size[1], size[2]);
}
};
class InstanceWriter
{
protected:
void add_material_bindings(COLLADASW::BindMaterial& bind_material, Object *ob)
{
for(int a = 0; a < ob->totcol; a++) {
Material *ma = give_current_material(ob, a+1);
COLLADASW::InstanceMaterialList& iml = bind_material.getInstanceMaterialList();
if (ma) {
std::string matid(id_name(ma));
COLLADASW::InstanceMaterial im(matid, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));
// create <bind_vertex_input> for each uv layer
Mesh *me = (Mesh*)ob->data;
int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
for (int b = 0; b < totlayer; b++) {
char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", b));
}
iml.push_back(im);
}
}
}
};
// XXX exporter writes wrong data for shared armatures. A separate
// controller should be written for each armature-mesh binding how do
// we make controller ids then?
class ArmatureExporter: public COLLADASW::LibraryControllers, protected TransformWriter, protected InstanceWriter
{
private:
Scene *scene;
public:
ArmatureExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryControllers(sw) {}
void export_armatures(Scene *sce)
// write bone nodes
void add_armature_bones(Object *ob_arm, Scene *sce)
{
// write bone nodes
bArmature *arm = (bArmature*)ob_arm->data;
for (Bone *bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
// start from root bones
if (!bone->parent)
add_bone_node(bone, ob_arm);
}
}
bool is_skinned_mesh(Object *ob)
{
return get_assigned_armature(ob) != NULL;
}
void add_instance_controller(Object *ob)
{
Object *ob_arm = get_assigned_armature(ob);
bArmature *arm = (bArmature*)ob_arm->data;
const std::string& controller_id = get_controller_id(ob_arm);
COLLADASW::InstanceController ins(mSW);
ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
// write root bone URLs
Bone *bone;
for (bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
if (!bone->parent)
ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_joint_id(bone, ob_arm)));
}
InstanceWriter::add_material_bindings(ins.getBindMaterial(), ob);
ins.add();
}
void export_controllers(Scene *sce)
{
scene = sce;
openLibrary();
forEachMeshObjectInScene(sce, *this);
@@ -575,8 +715,51 @@ public:
}
void operator()(Object *ob)
{
Object *ob_arm = get_assigned_armature(ob);
if (ob_arm /*&& !already_written(ob_arm)*/)
export_controller(ob, ob_arm);
}
private:
UnitConverter converter;
#if 0
std::vector<Object*> written_armatures;
bool already_written(Object *ob_arm)
{
return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != written_armatures.end();
}
void wrote(Object *ob_arm)
{
written_armatures.push_back(ob_arm);
}
void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce)
{
objects.clear();
Base *base= (Base*) sce->base.first;
while(base) {
Object *ob = base->object;
if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
objects.push_back(ob);
}
base= base->next;
}
}
#endif
Object *get_assigned_armature(Object *ob)
{
Object *ob_arm = NULL;
if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) {
ob_arm = ob->parent;
}
@@ -591,17 +774,82 @@ public:
}
}
if (ob_arm)
export_armature(ob, ob_arm);
return ob_arm;
}
private:
std::string get_joint_id(Bone *bone, Object *ob_arm)
{
return id_name(ob_arm) + "_" + bone->name;
}
UnitConverter converter;
std::string get_joint_sid(Bone *bone)
{
char name[100];
BLI_strncpy(name, bone->name, sizeof(name));
// these chars have special meaning in SID
replace_chars(name, ".()", '_');
return name;
}
// parent_mat is armature-space
void add_bone_node(Bone *bone, Object *ob_arm)
{
std::string node_id = get_joint_id(bone, ob_arm);
std::string node_name = std::string(bone->name);
std::string node_sid = get_joint_sid(bone);
COLLADASW::Node node(mSW);
node.setType(COLLADASW::Node::JOINT);
node.setNodeId(node_id);
node.setNodeName(node_name);
node.setNodeSid(node_sid);
node.start();
add_bone_transform(ob_arm, bone, node);
for (Bone *child = (Bone*)bone->childbase.first; child; child = child->next) {
add_bone_node(child, ob_arm);
}
node.end();
}
void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node)
{
bPose *pose = ob_arm->pose;
bPoseChannel *pchan = get_pose_channel(ob_arm->pose, bone->name);
float mat[4][4];
if (bone->parent) {
// get bone-space matrix from armature-space
bPoseChannel *parchan = get_pose_channel(ob_arm->pose, bone->parent->name);
float invpar[4][4];
Mat4Invert(invpar, parchan->pose_mat);
Mat4MulMat4(mat, pchan->pose_mat, invpar);
}
else {
// get world-space from armature-space
Mat4MulMat4(mat, pchan->pose_mat, ob_arm->obmat);
}
TransformWriter::add_node_transform(node, mat);
}
std::string get_controller_id(Object *ob_arm)
{
return id_name(ob_arm) + SKIN_CONTROLLER_ID_SUFFIX;
}
// ob should be of type OB_MESH
// both args are required
void export_armature(Object* ob, Object *ob_arm)
void export_controller(Object* ob, Object *ob_arm)
{
// joint names
// joint inverse bind matrices
@@ -629,20 +877,20 @@ private:
Mesh *me = (Mesh*)ob->data;
if (!me->dvert) return;
std::string controller_name(ob_arm->id.name);
std::string controller_id = controller_name + SKIN_CONTROLLER_ID_SUFFIX;
std::string controller_name = id_name(ob_arm);
std::string controller_id = get_controller_id(ob_arm);
openSkin(controller_id, controller_name, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob->id.name));
openSkin(controller_id, controller_name,
COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
add_bind_shape_mat(ob);
std::string joints_source_id = add_joints_source(&ob->defbase, controller_id);
std::string inv_bind_mat_source_id =
add_inv_bind_mats_source((bArmature*)ob_arm->data, &ob->defbase, controller_id);
std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id);
std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, &ob->defbase, controller_id);
std::string weights_source_id = add_weights_source(me, controller_id);
add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id);
add_vertex_weights_element(weights_source_id, joints_source_id, me);
add_vertex_weights_element(weights_source_id, joints_source_id, me, ob_arm, &ob->defbase);
closeSkin();
closeController();
@@ -664,25 +912,28 @@ private:
void add_bind_shape_mat(Object *ob)
{
float ob_bind_mat[4][4];
double dae_mat[4][4];
double bind_mat[4][4];
// TODO: get matrix from ob
Mat4One(ob_bind_mat);
converter.mat4_to_dae_double(bind_mat, ob->obmat);
converter.mat4_to_dae(dae_mat, ob_bind_mat);
addBindShapeTransform(dae_mat);
addBindShapeTransform(bind_mat);
}
std::string add_joints_source(ListBase *defbase, const std::string& controller_id)
std::string add_joints_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
{
std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX;
int totjoint = 0;
bDeformGroup *def;
for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
if (is_bone_defgroup(ob_arm, def))
totjoint++;
}
COLLADASW::NameSource source(mSW);
source.setId(source_id);
source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(BLI_countlist(defbase));
source.setAccessorCount(totjoint);
source.setAccessorStride(1);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@@ -690,10 +941,10 @@ private:
source.prepareToAppendValues();
bDeformGroup *def;
for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
source.appendValues(def->name);
Bone *bone = get_bone_from_defgroup(ob_arm, def);
if (bone)
source.appendValues(get_joint_sid(bone));
}
source.finish();
@@ -701,7 +952,7 @@ private:
return source_id;
}
std::string add_inv_bind_mats_source(bArmature *arm, ListBase *defbase, const std::string& controller_id)
std::string add_inv_bind_mats_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
{
std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX;
@@ -717,22 +968,40 @@ private:
source.prepareToAppendValues();
bDeformGroup *def;
bPose *pose = ob_arm->pose;
bArmature *arm = (bArmature*)ob_arm->data;
/*
Bone *get_named_bone (struct bArmature *arm, const char *name);
bPoseChannel *get_pose_channel(const struct bPose *pose, const char *name);
*/
int flag = arm->flag;
float inv_bind_mat[4][4];
Mat4One(inv_bind_mat);
// put armature in rest position
if (!(arm->flag & ARM_RESTPOS)) {
arm->flag |= ARM_RESTPOS;
where_is_pose(scene, ob_arm);
}
float dae_mat[4][4];
converter.mat4_to_dae(dae_mat, inv_bind_mat);
for (bDeformGroup *def = (bDeformGroup*)defbase->first; def; def = def->next) {
if (is_bone_defgroup(ob_arm, def)) {
// TODO: write inverse bind matrices for each bone (name taken from defbase)
for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
source.appendValues(dae_mat);
bPoseChannel *pchan = get_pose_channel(pose, def->name);
float mat[4][4];
float world[4][4];
float inv_bind_mat[4][4];
// make world-space matrix, pose_mat is armature-space
Mat4MulMat4(world, pchan->pose_mat, ob_arm->obmat);
Mat4Invert(mat, world);
converter.mat4_to_dae(inv_bind_mat, mat);
source.appendValues(inv_bind_mat);
}
}
// back from rest positon
if (!(flag & ARM_RESTPOS)) {
arm->flag = flag;
where_is_pose(scene, ob_arm);
}
source.finish();
@@ -740,14 +1009,32 @@ private:
return source_id;
}
Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup* def)
{
bPoseChannel *pchan = get_pose_channel(ob_arm->pose, def->name);
return pchan ? pchan->bone : NULL;
}
bool is_bone_defgroup(Object *ob_arm, bDeformGroup* def)
{
return get_bone_from_defgroup(ob_arm, def) != NULL;
}
std::string add_weights_source(Mesh *me, const std::string& controller_id)
{
std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX;
int i;
int totweight = 0;
for (i = 0; i < me->totvert; i++) {
totweight += me->dvert[i].totweight;
}
COLLADASW::FloatSourceF source(mSW);
source.setId(source_id);
source.setArrayId(source_id + ARRAY_ID_SUFFIX);
source.setAccessorCount(me->totvert);
source.setAccessorCount(totweight);
source.setAccessorStride(1);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@@ -757,7 +1044,7 @@ private:
// NOTE: COLLADA spec says weights should be normalized
for (int i = 0; i < me->totvert; i++) {
for (i = 0; i < me->totvert; i++) {
MDeformVert *vert = &me->dvert[i];
for (int j = 0; j < vert->totweight; j++) {
source.appendValues(vert->dw[j].weight);
@@ -769,7 +1056,8 @@ private:
return source_id;
}
void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me)
void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me,
Object *ob_arm, ListBase *defbase)
{
COLLADASW::VertexWeightsElement weights(mSW);
COLLADASW::InputList &input = weights.getInputList();
@@ -792,30 +1080,39 @@ private:
weights.prepareToAppendVCountValues();
weights.appendVertexCount(vcount);
std::vector<unsigned long> indices;
// def group index -> joint index
std::map<int, int> joint_index_by_def_index;
bDeformGroup *def;
int j;
for (def = (bDeformGroup*)defbase->first, i = 0, j = 0; def; def = def->next, i++) {
if (is_bone_defgroup(ob_arm, def))
joint_index_by_def_index[i] = j++;
else
joint_index_by_def_index[i] = -1;
}
weights.CloseVCountAndOpenVElement();
// write deformer index - weight index pairs
int weight_index = 0;
for (i = 0; i < me->totvert; i++) {
MDeformVert *dvert = &me->dvert[i];
for (int j = 0; j < dvert->totweight; j++) {
indices.push_back(dvert->dw[j].def_nr);
indices.push_back(weight_index++);
weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]);
weights.appendValues(weight_index++);
}
}
weights.CloseVCountAndOpenVElement();
weights.appendValues(indices);
weights.finish();
}
};
class SceneExporter: COLLADASW::LibraryVisualScenes
class SceneExporter: COLLADASW::LibraryVisualScenes, protected TransformWriter, protected InstanceWriter
{
ArmatureExporter *arm_exporter;
public:
SceneExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryVisualScenes(sw) {}
SceneExporter(COLLADASW::StreamWriter *sw, ArmatureExporter *arm) : COLLADASW::LibraryVisualScenes(sw),
arm_exporter(arm) {}
void exportScene(Scene *sce) {
// <library_visual_scenes> <visual_scene>
@@ -833,66 +1130,75 @@ public:
closeLibrary();
}
void exportHierarchy(Scene *sce)
{
Base *base= (Base*) sce->base.first;
while(base) {
Object *ob = base->object;
if (!ob->parent) {
switch(ob->type) {
case OB_MESH:
case OB_CAMERA:
case OB_LAMP:
case OB_EMPTY:
case OB_ARMATURE:
// write nodes....
writeNodes(ob, sce);
break;
}
}
base= base->next;
}
}
// called for each object
//void operator()(Object *ob) {
void writeNodes(Object *ob, Scene *sce) {
void writeNodes(Object *ob, Scene *sce)
{
COLLADASW::Node node(mSW);
node.setNodeId(ob->id.name);
node.setNodeId(id_name(ob));
node.setType(COLLADASW::Node::NODE);
std::string ob_name(id_name(ob));
node.start();
node.addTranslate("location", ob->loc[0], ob->loc[1], ob->loc[2]);
bool is_skinned_mesh = arm_exporter->is_skinned_mesh(ob);
float mat[4][4];
// this code used to create a single <rotate> representing object rotation
// float quat[4];
// float axis[3];
// float angle;
// double angle_deg;
// EulToQuat(ob->rot, quat);
// NormalQuat(quat);
// QuatToAxisAngle(quat, axis, &angle);
// angle_deg = angle * 180.0f / M_PI;
// node.addRotate(axis[0], axis[1], axis[2], angle_deg);
if (ob->type == OB_MESH && is_skinned_mesh)
// for skinned mesh we write obmat in <bind_shape_matrix>
Mat4One(mat);
else
Mat4CpyMat4(mat, ob->obmat);
float *rot = ob->rot;
node.addRotateX("rotationX", COLLADABU::Math::Utils::radToDegF(rot[0]));
node.addRotateY("rotationY", COLLADABU::Math::Utils::radToDegF(rot[1]));
node.addRotateZ("rotationZ", COLLADABU::Math::Utils::radToDegF(rot[2]));
node.addScale("scale", ob->size[0], ob->size[1], ob->size[2]);
TransformWriter::add_node_transform(node, mat);
// <instance_geometry>
if (ob->type == OB_MESH) {
COLLADASW::InstanceGeometry instGeom(mSW);
instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob_name));
for(int a = 0; a < ob->totcol; a++) {
Material *ma = give_current_material(ob, a+1);
COLLADASW::BindMaterial& bm = instGeom.getBindMaterial();
COLLADASW::InstanceMaterialList& iml = bm.getInstanceMaterialList();
if (ma) {
std::string matid(id_name(ma));
COLLADASW::InstanceMaterial im(matid, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));
// create <bind_vertex_input> for each uv layer
Mesh *me = (Mesh*)ob->data;
int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
for (int b = 0; b < totlayer; b++) {
char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", b));
}
iml.push_back(im);
}
if (is_skinned_mesh) {
arm_exporter->add_instance_controller(ob);
}
else {
COLLADASW::InstanceGeometry instGeom(mSW);
instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
InstanceWriter::add_material_bindings(instGeom.getBindMaterial(), ob);
instGeom.add();
instGeom.add();
}
}
// <instance_controller>
else if (ob->type == OB_ARMATURE) {
arm_exporter->add_armature_bones(ob, sce);
// XXX this looks unstable...
node.end();
}
// <instance_camera>
@@ -906,41 +1212,36 @@ public:
COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob_name));
instLa.add();
}
// empty object
else if (ob->type == OB_EMPTY) {
}
// write node for child object
// write nodes for child objects
Base *b = (Base*) sce->base.first;
while(b) {
// cob - child object
Object *cob = b->object;
if ((cob->type == OB_MESH || cob->type == OB_CAMERA || cob->type == OB_LAMP || cob->type == OB_EMPTY) && cob->parent == ob) {
// write node...
writeNodes(cob, sce);
if (cob->parent == ob) {
switch(cob->type) {
case OB_MESH:
case OB_CAMERA:
case OB_LAMP:
case OB_EMPTY:
case OB_ARMATURE:
// write node...
writeNodes(cob, sce);
break;
}
}
b = b->next;
}
node.end();
}
void exportHierarchy(Scene *sce)
{
Base *base= (Base*) sce->base.first;
while(base) {
Object *ob = base->object;
if ((ob->type == OB_MESH || ob->type == OB_CAMERA || ob->type == OB_LAMP || ob->type == OB_EMPTY) && !ob->parent) {
// write nodes....
writeNodes(ob, sce);
}
base= base->next;
}
if (ob->type != OB_ARMATURE)
node.end();
}
};
class ImagesExporter: COLLADASW::LibraryImages
@@ -1349,7 +1650,7 @@ public:
}
void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
Sampler::Semantic semantic, bool rotation, char *axis) {
Sampler::Semantic semantic, bool rotation, const char *axis) {
switch(semantic) {
case Sampler::INPUT:
param.push_back("TIME");
@@ -1394,7 +1695,7 @@ public:
}
}
std::string create_source(Sampler::Semantic semantic, FCurve *fcu, std::string& anim_id, char *axis_name)
std::string create_source(Sampler::Semantic semantic, FCurve *fcu, std::string& anim_id, const char *axis_name)
{
std::string source_id = anim_id + get_semantic_suffix(semantic);
@@ -1425,7 +1726,7 @@ public:
return source_id;
}
std::string create_interpolation_source(FCurve *fcu, std::string& anim_id, char *axis_name)
std::string create_interpolation_source(FCurve *fcu, std::string& anim_id, const char *axis_name)
{
std::string source_id = anim_id + get_semantic_suffix(Sampler::INTERPOLATION);
@@ -1452,7 +1753,7 @@ public:
return source_id;
}
std::string get_transform_sid(char *rna_path, char *axis_name)
std::string get_transform_sid(char *rna_path, const char *axis_name)
{
if (!strcmp(rna_path, "rotation"))
return std::string(rna_path) + axis_name;
@@ -1462,8 +1763,8 @@ public:
void add_animation(FCurve *fcu, const char *ob_name)
{
static char *axis_names[] = {"X", "Y", "Z"};
char *axis_name = NULL;
const char *axis_names[] = {"X", "Y", "Z"};
const char *axis_name = NULL;
char c_anim_id[100]; // careful!
if (fcu->array_index < 3)
@@ -1537,7 +1838,7 @@ void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename)
// <asset>
COLLADASW::Asset asset(&sw);
// XXX ask blender devs about this?
asset.setUnit("meter", 1.0);
asset.setUnit("decimetre", 0.1);
asset.setUpAxisType(COLLADASW::Asset::Z_UP);
asset.add();
@@ -1570,10 +1871,11 @@ void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename)
ae.exportAnimations(sce);
// <library_controllers>
ArmatureExporter(&sw).export_armatures(sce);
ArmatureExporter arm_exporter(&sw);
arm_exporter.export_controllers(sce);
// <library_visual_scenes>
SceneExporter se(&sw);
SceneExporter se(&sw, &arm_exporter);
se.exportScene(sce);
// <scene>

View File

@@ -139,8 +139,19 @@ const char *get_dae_name(T *node)
return name.size() ? name.c_str() : node->getOriginalId().c_str();
}
// use this for retrieving bone names, since these must be unique
template<class T>
const char *get_joint_name(T *node)
{
const std::string& id = node->getOriginalId();
return id.size() ? id.c_str() : node->getName().c_str();
}
float get_float_value(const COLLADAFW::FloatOrDoubleArray& array, int index)
{
if (index >= array.getValuesCount())
return 0.0f;
if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT)
return array.getFloatValues()->getData()[index];
else
@@ -156,7 +167,95 @@ public:
virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) = 0;
};
class ArmatureImporter
class TransformReader : protected TransformBase
{
protected:
UnitConverter *unit_converter;
struct Animation {
Object *ob;
COLLADAFW::Node *node;
COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id
};
public:
TransformReader(UnitConverter* conv) : unit_converter(conv) {}
void get_node_mat(float mat[][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map,
Object *ob)
{
float cur[4][4];
float copy[4][4];
Mat4One(mat);
for (int i = 0; i < node->getTransformations().getCount(); i++) {
COLLADAFW::Transformation *tm = node->getTransformations()[i];
COLLADAFW::Transformation::TransformationType type = tm->getTransformationType();
switch(type) {
case COLLADAFW::Transformation::TRANSLATE:
{
COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm;
COLLADABU::Math::Vector3& t = tra->getTranslation();
Mat4One(cur);
cur[3][0] = (float)t[0];
cur[3][1] = (float)t[1];
cur[3][2] = (float)t[2];
}
break;
case COLLADAFW::Transformation::ROTATE:
{
COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm;
COLLADABU::Math::Vector3& raxis = ro->getRotationAxis();
float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f);
float axis[] = {raxis[0], raxis[1], raxis[2]};
float quat[4];
float rot_copy[3][3];
float mat[3][3];
AxisAngleToQuat(quat, axis, angle);
QuatToMat4(quat, cur);
}
break;
case COLLADAFW::Transformation::SCALE:
{
COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale();
float size[3] = {(float)s[0], (float)s[1], (float)s[2]};
SizeToMat4(size, cur);
}
break;
case COLLADAFW::Transformation::MATRIX:
{
unit_converter->mat4_from_dae(cur, ((COLLADAFW::Matrix*)tm)->getMatrix());
}
break;
case COLLADAFW::Transformation::LOOKAT:
case COLLADAFW::Transformation::SKEW:
fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n");
break;
}
Mat4CpyMat4(copy, mat);
Mat4MulMat4(mat, cur, copy);
if (animation_map) {
// AnimationList that drives this Transformation
const COLLADAFW::UniqueId& anim_list_id = tm->getAnimationList();
// store this so later we can link animation data with ob
Animation anim = {ob, node, tm};
(*animation_map)[anim_list_id] = anim;
}
}
}
};
class ArmatureImporter : private TransformReader
{
private:
Scene *scene;
@@ -210,7 +309,7 @@ private:
// data from COLLADAFW::SkinControllerData, each array should be freed
COLLADAFW::UIntValuesArray joints_per_vertex;
COLLADAFW::UIntValuesArray weight_indices;
COLLADAFW::UIntValuesArray joint_indices;
COLLADAFW::IntValuesArray joint_indices;
// COLLADAFW::FloatOrDoubleArray weights;
std::vector<float> weights;
@@ -220,6 +319,7 @@ private:
Object *ob_arm;
COLLADAFW::UniqueId controller_uid;
public:
SkinInfo() {}
@@ -232,23 +332,30 @@ private:
{
Mat4CpyMat4(bind_shape_matrix, (float (*)[4])skin.bind_shape_matrix);
transfer_array_data_const(skin.joints_per_vertex, joints_per_vertex);
transfer_array_data_const(skin.weight_indices, weight_indices);
transfer_array_data_const(skin.joint_indices, joint_indices);
transfer_uint_array_data_const(skin.joints_per_vertex, joints_per_vertex);
transfer_uint_array_data_const(skin.weight_indices, weight_indices);
transfer_int_array_data_const(skin.joint_indices, joint_indices);
}
SkinInfo(UnitConverter *conv) : unit_converter(conv), ob_arm(NULL) {}
// nobody owns the data after this, so it should be freed manually with releaseMemory
void transfer_array_data(COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest)
template <class T>
void transfer_array_data(T& src, T& dest)
{
dest.setData((unsigned int*)src.getData(), src.getCount());
dest.setData(src.getData(), src.getCount());
src.yieldOwnerShip();
dest.yieldOwnerShip();
}
// when src is const we cannot src.yieldOwnerShip, this is used by copy constructor
void transfer_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest)
void transfer_int_array_data_const(const COLLADAFW::IntValuesArray& src, COLLADAFW::IntValuesArray& dest)
{
dest.setData((int*)src.getData(), src.getCount());
dest.yieldOwnerShip();
}
void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest)
{
dest.setData((unsigned int*)src.getData(), src.getCount());
dest.yieldOwnerShip();
@@ -258,7 +365,7 @@ private:
{
transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getJointsPerVertex(), joints_per_vertex);
transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getWeightIndices(), weight_indices);
transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getJointIndices(), joint_indices);
transfer_array_data((COLLADAFW::IntValuesArray&)skin->getJointIndices(), joint_indices);
// transfer_array_data(skin->getWeights(), weights);
// cannot transfer data for FloatOrDoubleArray, copy values manually
@@ -374,7 +481,7 @@ private:
// name group by joint node name
if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) {
name = get_dae_name(joint_by_uid[(*it).joint_uid]);
name = get_joint_name(joint_by_uid[(*it).joint_uid]);
}
add_defgroup_name(ob, (char*)name);
@@ -410,6 +517,11 @@ private:
ED_anim_dag_flush_update(C);
WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
}
bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node)
{
return get_pose_channel(ob_arm->pose, get_joint_name(node));
}
};
std::map<COLLADAFW::UniqueId, SkinInfo> skin_by_data_uid; // data UID = skin controller data UID
@@ -450,7 +562,7 @@ private:
float obmat[4][4];
// object-space
get_node_mat(obmat, node);
get_node_mat(obmat, node, NULL, NULL);
// get world-space
if (parent)
@@ -460,7 +572,7 @@ private:
}
// TODO rename from Node "name" attrs later
EditBone *bone = addEditBone(arm, (char*)get_dae_name(node));
EditBone *bone = addEditBone(arm, (char*)get_joint_name(node));
totbone++;
if (parent) bone->parent = parent;
@@ -476,6 +588,7 @@ private:
if (parent && totchild == 1) {
VecCopyf(parent->tail, bone->head);
// XXX increase this to prevent "very" small bones?
const float epsilon = 0.000001f;
// derive leaf bone length
@@ -539,67 +652,6 @@ private:
leaf_bones.push_back(leaf);
}
void get_node_mat(float mat[][4], COLLADAFW::Node *node)
{
float cur[4][4];
float copy[4][4];
Mat4One(mat);
for (int i = 0; i < node->getTransformations().getCount(); i++) {
COLLADAFW::Transformation *tm = node->getTransformations()[i];
COLLADAFW::Transformation::TransformationType type = tm->getTransformationType();
switch(type) {
case COLLADAFW::Transformation::TRANSLATE:
{
COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm;
COLLADABU::Math::Vector3& t = tra->getTranslation();
Mat4One(cur);
cur[3][0] = (float)t[0];
cur[3][1] = (float)t[1];
cur[3][2] = (float)t[2];
}
break;
case COLLADAFW::Transformation::ROTATE:
{
COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm;
COLLADABU::Math::Vector3& raxis = ro->getRotationAxis();
float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f);
float axis[] = {raxis[0], raxis[1], raxis[2]};
float quat[4];
float rot_copy[3][3];
float mat[3][3];
AxisAngleToQuat(quat, axis, angle);
QuatToMat4(quat, cur);
}
break;
case COLLADAFW::Transformation::SCALE:
{
COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale();
float size[3] = {(float)s[0], (float)s[1], (float)s[2]};
SizeToMat4(size, cur);
}
break;
case COLLADAFW::Transformation::MATRIX:
{
unit_converter->mat4_from_dae(cur, ((COLLADAFW::Matrix*)tm)->getMatrix());
}
break;
case COLLADAFW::Transformation::LOOKAT:
case COLLADAFW::Transformation::SKEW:
fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n");
break;
}
Mat4CpyMat4(copy, mat);
Mat4MulMat4(mat, cur, copy);
}
}
void fix_leaf_bones()
{
// just setting tail for leaf bones here
@@ -634,7 +686,37 @@ private:
fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name);
}
}
}
void set_euler_rotmode()
{
// just set rotmode = PCHAN_ROT_EUL on pose channel for each joint
std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>::iterator it;
for (it = joint_by_uid.begin(); it != joint_by_uid.end(); it++) {
COLLADAFW::Node *joint = it->second;
std::map<COLLADAFW::UniqueId, SkinInfo>::iterator sit;
for (sit = skin_by_data_uid.begin(); sit != skin_by_data_uid.end(); sit++) {
SkinInfo& skin = sit->second;
if (skin.uses_joint(joint)) {
bPoseChannel *pchan = skin.get_pose_channel_from_node(joint);
if (pchan) {
pchan->rotmode = PCHAN_ROT_EUL;
}
else {
fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint));
}
break;
}
}
}
}
Object *get_empty_for_leaves()
@@ -717,12 +799,15 @@ private:
DAG_object_flush_update(scene, ob_arm, OB_RECALC_OB|OB_RECALC_DATA);
set_leaf_bone_shapes(ob_arm);
set_euler_rotmode();
}
public:
ArmatureImporter(UnitConverter *conv, MeshImporterBase *imp, Scene *sce) :
unit_converter(conv), scene(sce), empty(NULL), mesh_importer(imp) {}
TransformReader(conv), scene(sce), empty(NULL), mesh_importer(imp) {}
~ArmatureImporter()
{
@@ -752,7 +837,7 @@ public:
}
#ifdef COLLADA_DEBUG
else {
fprintf(stderr, "%s cannot be added to armature.\n", get_dae_name(node));
fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node));
}
#endif
}
@@ -761,13 +846,6 @@ public:
// here we add bones to armatures, having armatures previously created in write_controller
void make_armatures(bContext *C)
{
#if 0
std::vector<ArmatureJoints>::iterator it;
for (it = armature_joints.begin(); it != armature_joints.end(); it++) {
create_armature_bones(*it);
}
#endif
std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it;
for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) {
@@ -873,6 +951,24 @@ public:
return &geom_uid_by_controller_uid[controller_uid];
}
Object *get_armature_for_joint(COLLADAFW::Node *node)
{
std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it;
for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) {
SkinInfo& skin = it->second;
if (skin.uses_joint(node))
return skin.get_armature();
}
return NULL;
}
void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count)
{
BLI_snprintf(joint_path, count, "pose.pose_channels[\"%s\"]", get_joint_name(node));
}
};
class MeshImporter : public MeshImporterBase
@@ -1548,6 +1644,371 @@ public:
};
class AnimationImporter : private TransformReader
{
private:
ArmatureImporter *armature_importer;
Scene *scene;
std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
std::map<COLLADAFW::UniqueId, TransformReader::Animation> uid_animated_map;
void make_fcurves_from_animation(COLLADAFW::AnimationCurve *curve,
COLLADAFW::FloatOrDoubleArray& input,
COLLADAFW::FloatOrDoubleArray& output,
COLLADAFW::FloatOrDoubleArray& intan,
COLLADAFW::FloatOrDoubleArray& outtan, size_t dim, float fps)
{
int i;
// char *path = "location";
std::vector<FCurve*>& fcurves = uid_fcurve_map[curve->getUniqueId()];
if (dim == 1) {
// create fcurve
FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
// fcu->rna_path = BLI_strdupn(path, strlen(path));
fcu->array_index = 0;
fcu->totvert = curve->getKeyCount();
// create beztriple for each key
for (i = 0; i < curve->getKeyCount(); i++) {
BezTriple bez;
memset(&bez, 0, sizeof(BezTriple));
// intangent
bez.vec[0][0] = get_float_value(intan, i + i) * fps;
bez.vec[0][1] = get_float_value(intan, i + i + 1);
// input, output
bez.vec[1][0] = get_float_value(input, i) * fps;
bez.vec[1][1] = get_float_value(output, i);
// outtangent
bez.vec[2][0] = get_float_value(outtan, i + i) * fps;
bez.vec[2][1] = get_float_value(outtan, i + i + 1);
bez.ipo = U.ipo_new; /* use default interpolation mode here... */
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez);
calchandles_fcurve(fcu);
}
fcurves.push_back(fcu);
}
else if(dim == 3) {
for (i = 0; i < dim; i++ ) {
// create fcurve
FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
// fcu->rna_path = BLI_strdupn(path, strlen(path));
fcu->array_index = 0;
fcu->totvert = curve->getKeyCount();
// create beztriple for each key
for (int j = 0; j < curve->getKeyCount(); j++) {
BezTriple bez;
memset(&bez, 0, sizeof(BezTriple));
// intangent
bez.vec[0][0] = get_float_value(intan, j * 6 + i + i) * fps;
bez.vec[0][1] = get_float_value(intan, j * 6 + i + i + 1);
// input, output
bez.vec[1][0] = get_float_value(input, j) * fps;
bez.vec[1][1] = get_float_value(output, j * 3 + i);
// outtangent
bez.vec[2][0] = get_float_value(outtan, j * 6 + i + i) * fps;
bez.vec[2][1] = get_float_value(outtan, j * 6 + i + i + 1);
bez.ipo = U.ipo_new; /* use default interpolation mode here... */
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez);
calchandles_fcurve(fcu);
}
fcurves.push_back(fcu);
}
}
}
void add_fcurves_to_object(Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index)
{
ID *id = &ob->id;
bAction *act;
if (!ob->adt || !ob->adt->action)
act = verify_adt_action(id, 1);
else
act = verify_adt_action(id, 0);
if (!ob->adt || !ob->adt->action) {
fprintf(stderr, "Cannot create anim data or action for this object. \n");
return;
}
FCurve *fcu;
std::vector<FCurve*>::iterator it;
int i = 0;
for (it = curves.begin(); it != curves.end(); it++) {
fcu = *it;
fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
if (array_index == -1)
fcu->array_index = i;
else
fcu->array_index = array_index;
// convert degrees to radians for rotation
char *p = strstr(rna_path, "rotation");
if (p && *(p + strlen("rotation")) == '\0') {
for(int j = 0; j < fcu->totvert; j++) {
float rot_intan = fcu->bezt[j].vec[0][1];
float rot_output = fcu->bezt[j].vec[1][1];
float rot_outtan = fcu->bezt[j].vec[2][1];
fcu->bezt[j].vec[0][1] = rot_intan * M_PI / 180.0f;
fcu->bezt[j].vec[1][1] = rot_output * M_PI / 180.0f;
fcu->bezt[j].vec[2][1] = rot_outtan * M_PI / 180.0f;
}
}
i++;
BLI_addtail(&act->curves, fcu);
}
}
public:
AnimationImporter(UnitConverter *conv, ArmatureImporter *arm, Scene *scene) :
TransformReader(conv), armature_importer(arm), scene(scene) { }
bool write_animation( const COLLADAFW::Animation* anim )
{
float fps = (float)FPS;
if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) {
COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim;
size_t dim = curve->getOutDimension();
// XXX Don't know if it's necessary
// Should we check outPhysicalDimension?
if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) {
fprintf(stderr, "Inputs physical dimension is not time. \n");
return true;
}
COLLADAFW::FloatOrDoubleArray& input = curve->getInputValues();
COLLADAFW::FloatOrDoubleArray& output = curve->getOutputValues();
COLLADAFW::FloatOrDoubleArray& intan = curve->getInTangentValues();
COLLADAFW::FloatOrDoubleArray& outtan = curve->getOutTangentValues();
// a curve can have mixed interpolation type,
// in this case curve->getInterpolationTypes returns a list of interpolation types per key
COLLADAFW::AnimationCurve::InterpolationType interp = curve->getInterpolationType();
if (interp != COLLADAFW::AnimationCurve::INTERPOLATION_MIXED) {
switch (interp) {
case COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR:
// support this
make_fcurves_from_animation(curve, input, output, intan, outtan, dim, fps);
break;
case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER:
// and this
make_fcurves_from_animation(curve, input, output, intan, outtan, dim, fps);
break;
case COLLADAFW::AnimationCurve::INTERPOLATION_CARDINAL:
case COLLADAFW::AnimationCurve::INTERPOLATION_HERMITE:
case COLLADAFW::AnimationCurve::INTERPOLATION_BSPLINE:
case COLLADAFW::AnimationCurve::INTERPOLATION_STEP:
fprintf(stderr, "CARDINAL, HERMITE, BSPLINE and STEP anim interpolation types not supported yet.\n");
break;
}
}
else {
// not supported yet
fprintf(stderr, "MIXED anim interpolation type is not supported yet.\n");
}
}
else {
fprintf(stderr, "FORMULA animation type is not supported yet.\n");
}
return true;
}
// called on post-process stage after writeVisualScenes
bool write_animation_list( const COLLADAFW::AnimationList* animationList )
{
const COLLADAFW::UniqueId& anim_list_id = animationList->getUniqueId();
// possible in case we cannot interpret some transform
if (uid_animated_map.find(anim_list_id) == uid_animated_map.end()) {
return true;
}
// for bones rna_path is like: pose.pose_channels["bone-name"].rotation
// what does this AnimationList animate?
Animation& animated = uid_animated_map[anim_list_id];
Object *ob = animated.ob;
char rna_path[100];
char joint_path[100];
bool is_joint = false;
// if ob is NULL, it should be a JOINT
if (!ob) {
ob = armature_importer->get_armature_for_joint(animated.node);
if (!ob) {
fprintf(stderr, "Cannot find armature for node %s\n", get_joint_name(animated.node));
return true;
}
armature_importer->get_rna_path_for_joint(animated.node, joint_path, sizeof(joint_path));
is_joint = true;
}
const COLLADAFW::AnimationList::AnimationBindings& bindings = animationList->getAnimationBindings();
switch (animated.tm->getTransformationType()) {
case COLLADAFW::Transformation::TRANSLATE:
{
if (is_joint)
BLI_snprintf(rna_path, sizeof(rna_path), "%s.location", joint_path);
else
BLI_strncpy(rna_path, "location", sizeof(rna_path));
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
fprintf(stderr, "Cannot find FCurve by animation UID.\n");
continue;
}
std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
switch (binding.animationClass) {
case COLLADAFW::AnimationList::POSITION_X:
add_fcurves_to_object(ob, fcurves, rna_path, 0);
break;
case COLLADAFW::AnimationList::POSITION_Y:
add_fcurves_to_object(ob, fcurves, rna_path, 1);
break;
case COLLADAFW::AnimationList::POSITION_Z:
add_fcurves_to_object(ob, fcurves, rna_path, 2);
break;
case COLLADAFW::AnimationList::POSITION_XYZ:
add_fcurves_to_object(ob, fcurves, rna_path, -1);
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n",
binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::ROTATE:
{
if (is_joint)
BLI_snprintf(rna_path, sizeof(rna_path), "%s.euler_rotation", joint_path);
else
BLI_strncpy(rna_path, "rotation", sizeof(rna_path));
COLLADAFW::Rotate* rot = (COLLADAFW::Rotate*)animated.tm;
COLLADABU::Math::Vector3& axis = rot->getRotationAxis();
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
fprintf(stderr, "Cannot find FCurve by animation UID.\n");
continue;
}
std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
switch (binding.animationClass) {
case COLLADAFW::AnimationList::ANGLE:
if (COLLADABU::Math::Vector3::UNIT_X == axis) {
add_fcurves_to_object(ob, fcurves, rna_path, 0);
}
else if (COLLADABU::Math::Vector3::UNIT_Y == axis) {
add_fcurves_to_object(ob, fcurves, rna_path, 1);
}
else if (COLLADABU::Math::Vector3::UNIT_Z == axis) {
add_fcurves_to_object(ob, fcurves, rna_path, 2);
}
break;
case COLLADAFW::AnimationList::AXISANGLE:
// convert axis-angle to quat? or XYZ?
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for ROTATE transformation.\n",
binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::SCALE:
{
if (is_joint)
BLI_snprintf(rna_path, sizeof(rna_path), "%s.scale", joint_path);
else
BLI_strncpy(rna_path, "scale", sizeof(rna_path));
// same as for TRANSLATE
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
fprintf(stderr, "Cannot find FCurve by animation UID.\n");
continue;
}
std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
switch (binding.animationClass) {
case COLLADAFW::AnimationList::POSITION_X:
add_fcurves_to_object(ob, fcurves, rna_path, 0);
break;
case COLLADAFW::AnimationList::POSITION_Y:
add_fcurves_to_object(ob, fcurves, rna_path, 1);
break;
case COLLADAFW::AnimationList::POSITION_Z:
add_fcurves_to_object(ob, fcurves, rna_path, 2);
break;
case COLLADAFW::AnimationList::POSITION_XYZ:
add_fcurves_to_object(ob, fcurves, rna_path, -1);
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n",
binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::MATRIX:
case COLLADAFW::Transformation::SKEW:
case COLLADAFW::Transformation::LOOKAT:
fprintf(stderr, "Animation of MATRIX, SKEW and LOOKAT transformations is not supported yet.\n");
break;
}
return true;
}
void read_node_transform(COLLADAFW::Node *node, Object *ob)
{
float mat[4][4];
TransformReader::get_node_mat(mat, node, &uid_animated_map, ob);
if (ob)
TransformReader::decompose(mat, ob->loc, ob->rot, ob->size);
}
};
/*
COLLADA Importer limitations:
@@ -1567,6 +2028,7 @@ private:
UnitConverter unit_converter;
ArmatureImporter armature_importer;
MeshImporter mesh_importer;
AnimationImporter anim_importer;
std::map<COLLADAFW::UniqueId, Image*> uid_image_map;
std::map<COLLADAFW::UniqueId, Material*> uid_material_map;
@@ -1577,21 +2039,17 @@ private:
//std::map<COLLADAFW::TextureMapId, char*> set_layername_map;
std::map<Material*, TexIndexTextureArrayMap> material_texture_mapping_map;
// animation
std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
struct AnimatedTransform {
Object *ob;
// COLLADAFW::Node *node;
COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id
};
// std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
// Nodes don't share AnimationLists (Arystan)
std::map<COLLADAFW::UniqueId, AnimatedTransform> uid_animated_map; // AnimationList->uniqueId to AnimatedObject map
// std::map<COLLADAFW::UniqueId, Animation> uid_animated_map; // AnimationList->uniqueId to AnimatedObject map
public:
/** Constructor. */
Writer(bContext *C, const char *filename) : mContext(C), mFilename(filename),
armature_importer(&unit_converter, &mesh_importer, CTX_data_scene(C)),
mesh_importer(&armature_importer, CTX_data_scene(C)) {};
mesh_importer(&armature_importer, CTX_data_scene(C)),
anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) {}
/** Destructor. */
~Writer() {};
@@ -1654,165 +2112,95 @@ public:
void write_node (COLLADAFW::Node *node, COLLADAFW::Node *parent_node, Scene *sce, Object *par)
{
// XXX linking object with the first <instance_geometry>, though a node may have more of them...
// maybe join multiple <instance_...> meshes into 1, and link object with it? not sure...
if (node->getType() != COLLADAFW::Node::NODE) {
Object *ob = NULL;
if (node->getType() == COLLADAFW::Node::JOINT) {
if (node->getType() == COLLADAFW::Node::JOINT) {
armature_importer.add_joint(node, parent_node == NULL || parent_node->getType() != COLLADAFW::Node::JOINT);
}
COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes();
for (int i = 0; i < child_nodes.getCount(); i++) {
write_node(child_nodes[i], node, sce, NULL);
}
return;
}
COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries();
COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras();
COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights();
COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers();
COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes();
Object *ob = NULL;
int k;
// <instance_geometry>
if (geom.getCount() != 0) {
ob = mesh_importer.create_mesh_object(node, geom[0], false, uid_material_map, material_texture_mapping_map);
}
// <instance_camera>
else if (camera.getCount() != 0) {
const COLLADAFW::UniqueId& cam_uid = camera[0]->getInstanciatedObjectId();
if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) {
fprintf(stderr, "Couldn't find camera by UID. \n");
return;
}
ob = add_object(sce, OB_CAMERA);
Camera *cam = uid_camera_map[cam_uid];
Camera *old_cam = (Camera*)ob->data;
old_cam->id.us--;
ob->data = cam;
if (old_cam->id.us == 0) free_libblock(&G.main->camera, old_cam);
}
// <instance_light>
else if (lamp.getCount() != 0) {
const COLLADAFW::UniqueId& lamp_uid = lamp[0]->getInstanciatedObjectId();
if (uid_lamp_map.find(lamp_uid) == uid_lamp_map.end()) {
fprintf(stderr, "Couldn't find lamp by UID. \n");
return;
}
ob = add_object(sce, OB_LAMP);
Lamp *la = uid_lamp_map[lamp_uid];
Lamp *old_lamp = (Lamp*)ob->data;
old_lamp->id.us--;
ob->data = la;
if (old_lamp->id.us == 0) free_libblock(&G.main->lamp, old_lamp);
}
// <instance_controller>
else if (controller.getCount() != 0) {
COLLADAFW::InstanceController *geom = (COLLADAFW::InstanceController*)controller[0];
ob = mesh_importer.create_mesh_object(node, geom, true, uid_material_map, material_texture_mapping_map);
}
// XXX <node> - this is not supported yet
else if (inst_node.getCount() != 0) {
return;
}
// if node is empty - create empty object
// XXX empty node may not mean it is empty object, not sure about this
else {
ob = add_object(sce, OB_EMPTY);
}
// just checking if object wasn't created
if (ob == NULL) return;
// if par was given make this object child of the previous
if (par != NULL) {
Object workob;
COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries();
COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras();
COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights();
COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers();
COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes();
ob->parent = par;
// doing what 'set parent' operator does
par->recalc |= OB_RECALC_OB;
ob->parsubstr[0] = 0;
DAG_scene_sort(sce);
// since ob->obmat is identity, this is not needed?
/*what_does_parent(sce, ob, &workob);
Mat4Invert(ob->parentinv, workob.obmat);
ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA;
ob->partype = PAROBJECT;
DAG_scene_sort(sce);*/
}
// transform Object
float rot[3][3];
Mat3One(rot);
// transform Object and store animation linking info
// TODO: apply transforms sequentially and then extract location/scale/rotation from final matrix
// this will be the correct way
for (k = 0; k < node->getTransformations().getCount(); k ++) {
COLLADAFW::Transformation *tm = node->getTransformations()[k];
COLLADAFW::Transformation::TransformationType type = tm->getTransformationType();
switch(type) {
case COLLADAFW::Transformation::TRANSLATE:
{
COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm;
COLLADABU::Math::Vector3& t = tra->getTranslation();
ob->loc[0] = (float)t[0];
ob->loc[1] = (float)t[1];
ob->loc[2] = (float)t[2];
}
break;
case COLLADAFW::Transformation::ROTATE:
{
COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm;
COLLADABU::Math::Vector3& raxis = ro->getRotationAxis();
float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f);
float axis[] = {raxis[0], raxis[1], raxis[2]};
float quat[4];
float rot_copy[3][3];
float mat[3][3];
AxisAngleToQuat(quat, axis, angle);
QuatToMat3(quat, mat);
Mat3CpyMat3(rot_copy, rot);
Mat3MulMat3(rot, mat, rot_copy);
}
break;
case COLLADAFW::Transformation::SCALE:
{
COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale();
ob->size[0] = (float)s[0];
ob->size[1] = (float)s[1];
ob->size[2] = (float)s[2];
}
break;
case COLLADAFW::Transformation::MATRIX:
case COLLADAFW::Transformation::LOOKAT:
case COLLADAFW::Transformation::SKEW:
fprintf(stderr, "MATRIX, LOOKAT and SKEW transformations are not supported yet.\n");
break;
// XXX linking object with the first <instance_geometry>, though a node may have more of them...
// maybe join multiple <instance_...> meshes into 1, and link object with it? not sure...
// <instance_geometry>
if (geom.getCount() != 0) {
ob = mesh_importer.create_mesh_object(node, geom[0], false, uid_material_map, material_texture_mapping_map);
}
else if (camera.getCount() != 0) {
const COLLADAFW::UniqueId& cam_uid = camera[0]->getInstanciatedObjectId();
if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) {
fprintf(stderr, "Couldn't find camera by UID. \n");
return;
}
ob = add_object(sce, OB_CAMERA);
Camera *cam = uid_camera_map[cam_uid];
Camera *old_cam = (Camera*)ob->data;
old_cam->id.us--;
ob->data = cam;
if (old_cam->id.us == 0) free_libblock(&G.main->camera, old_cam);
}
else if (lamp.getCount() != 0) {
const COLLADAFW::UniqueId& lamp_uid = lamp[0]->getInstanciatedObjectId();
if (uid_lamp_map.find(lamp_uid) == uid_lamp_map.end()) {
fprintf(stderr, "Couldn't find lamp by UID. \n");
return;
}
ob = add_object(sce, OB_LAMP);
Lamp *la = uid_lamp_map[lamp_uid];
Lamp *old_lamp = (Lamp*)ob->data;
old_lamp->id.us--;
ob->data = la;
if (old_lamp->id.us == 0) free_libblock(&G.main->lamp, old_lamp);
}
else if (controller.getCount() != 0) {
COLLADAFW::InstanceController *geom = (COLLADAFW::InstanceController*)controller[0];
ob = mesh_importer.create_mesh_object(node, geom, true, uid_material_map, material_texture_mapping_map);
}
// XXX instance_node is not supported yet
else if (inst_node.getCount() != 0) {
return;
}
// if node is empty - create empty object
// XXX empty node may not mean it is empty object, not sure about this
else {
ob = add_object(sce, OB_EMPTY);
}
// if par was given make this object child of the previous
if (par && ob) {
Object workob;
ob->parent = par;
// doing what 'set parent' operator does
par->recalc |= OB_RECALC_OB;
ob->parsubstr[0] = 0;
// AnimationList that drives this Transformation
const COLLADAFW::UniqueId& anim_list_id = tm->getAnimationList();
// store this so later we can link animation data with ob
AnimatedTransform anim = {ob, tm};
this->uid_animated_map[anim_list_id] = anim;
DAG_scene_sort(sce);
// since ob->obmat is identity, this is not needed?
/*what_does_parent(sce, ob, &workob);
Mat4Invert(ob->parentinv, workob.obmat);
ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA;
ob->partype = PAROBJECT;
DAG_scene_sort(sce);*/
}
}
Mat3ToEul(rot, ob->rot);
anim_importer.read_node_transform(node, ob);
// if node has child nodes write them
COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes();
for (k = 0; k < child_nodes.getCount(); k++) {
COLLADAFW::Node *child_node = child_nodes[k];
write_node(child_node, node, sce, ob);
for (int i = 0; i < child_nodes.getCount(); i++) {
write_node(child_nodes[i], node, sce, ob);
}
}
@@ -2148,318 +2536,16 @@ public:
return true;
}
float get_float(COLLADAFW::FloatOrDoubleArray array, int i)
{
switch(array.getType()) {
case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT:
{
COLLADAFW::ArrayPrimitiveType<float> *values = array.getFloatValues();
if (!values->empty())
return (*values)[i];
else return 0;
}
break;
case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE:
{
COLLADAFW::ArrayPrimitiveType<double> *values = array.getDoubleValues();
if (!values->empty())
return (float)(*values)[i];
else return 0;
}
break;
}
}
void write_curves(const COLLADAFW::Animation* anim,
COLLADAFW::AnimationCurve *curve,
COLLADAFW::FloatOrDoubleArray input,
COLLADAFW::FloatOrDoubleArray output,
COLLADAFW::FloatOrDoubleArray intan,
COLLADAFW::FloatOrDoubleArray outtan, size_t dim, float fps)
{
int i;
char *path = "location";
if (dim == 1) {
// create fcurve
FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
if (!fcu) {
fprintf(stderr, "Cannot create fcurve. \n");
return;
}
fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
fcu->rna_path = BLI_strdupn(path, strlen(path));
fcu->array_index = 0;
fcu->totvert = curve->getKeyCount();
// create beztriple for each key
for (i = 0; i < curve->getKeyCount(); i++) {
BezTriple bez;
memset(&bez, 0, sizeof(BezTriple));
// intangent
bez.vec[0][0] = get_float(intan, i + i) * fps;
bez.vec[0][1] = get_float(intan, i + i + 1);
// input, output
bez.vec[1][0] = get_float(input, i) * fps;
bez.vec[1][1] = get_float(output, i);
// outtangent
bez.vec[2][0] = get_float(outtan, i + i) * fps;
bez.vec[2][1] = get_float(outtan, i + i + 1);
bez.ipo = U.ipo_new; /* use default interpolation mode here... */
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez);
calchandles_fcurve(fcu);
}
// map fcurve to animation's UID
this->uid_fcurve_map[anim->getUniqueId()].push_back(fcu);
}
else if(dim == 3) {
for (i = 0; i < dim; i++ ) {
// create fcurve
FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
if (!fcu) {
fprintf(stderr, "Cannot create fcurve. \n");
continue;
}
fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
fcu->rna_path = BLI_strdupn(path, strlen(path));
fcu->array_index = 0;
fcu->totvert = curve->getKeyCount();
// create beztriple for each key
for (int j = 0; j < curve->getKeyCount(); j++) {
BezTriple bez;
memset(&bez, 0, sizeof(BezTriple));
// intangent
bez.vec[0][0] = get_float(intan, j * 6 + i + i) * fps;
bez.vec[0][1] = get_float(intan, j * 6 + i + i + 1);
// input, output
bez.vec[1][0] = get_float(input, j) * fps;
bez.vec[1][1] = get_float(output, j * 3 + i);
// outtangent
bez.vec[2][0] = get_float(outtan, j * 6 + i + i) * fps;
bez.vec[2][1] = get_float(outtan, j * 6 + i + i + 1);
bez.ipo = U.ipo_new; /* use default interpolation mode here... */
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez);
calchandles_fcurve(fcu);
}
// map fcurve to animation's UID
this->uid_fcurve_map[anim->getUniqueId()].push_back(fcu);
}
}
}
// this function is called only for animations that pass COLLADAFW::validate
virtual bool writeAnimation( const COLLADAFW::Animation* anim )
{
if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) {
COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim;
Scene *scene = CTX_data_scene(mContext);
float fps = (float)FPS;
// I wonder how do we use this (Arystan)
size_t dim = curve->getOutDimension();
// XXX Don't know if it's necessary
// Should we check outPhysicalDimension?
if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) {
fprintf(stderr, "Inputs physical dimension is not time. \n");
return true;
}
COLLADAFW::FloatOrDoubleArray input = curve->getInputValues();
COLLADAFW::FloatOrDoubleArray output = curve->getOutputValues();
COLLADAFW::FloatOrDoubleArray intan = curve->getInTangentValues();
COLLADAFW::FloatOrDoubleArray outtan = curve->getOutTangentValues();
// a curve can have mixed interpolation type,
// in this case curve->getInterpolationTypes returns a list of interpolation types per key
COLLADAFW::AnimationCurve::InterpolationType interp = curve->getInterpolationType();
if (interp != COLLADAFW::AnimationCurve::INTERPOLATION_MIXED) {
switch (interp) {
case COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR:
// support this
write_curves(anim, curve, input, output, intan, outtan, dim, fps);
break;
case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER:
// and this
write_curves(anim, curve, input, output, intan, outtan, dim, fps);
break;
case COLLADAFW::AnimationCurve::INTERPOLATION_CARDINAL:
case COLLADAFW::AnimationCurve::INTERPOLATION_HERMITE:
case COLLADAFW::AnimationCurve::INTERPOLATION_BSPLINE:
case COLLADAFW::AnimationCurve::INTERPOLATION_STEP:
fprintf(stderr, "CARDINAL, HERMITE, BSPLINE and STEP anim interpolation types not supported yet.\n");
break;
}
}
else {
// not supported yet
fprintf(stderr, "MIXED anim interpolation type is not supported yet.\n");
}
}
else {
fprintf(stderr, "FORMULA animation type is not supported yet.\n");
}
return true;
}
void change_fcurve(Object *ob, const COLLADAFW::UniqueId& anim_id, char *rna_path, int array_index)
{
if (uid_fcurve_map.find(anim_id) == uid_fcurve_map.end()) {
fprintf(stderr, "Cannot find fcurves by UID.\n");
return;
}
ID *id = &ob->id;
bAction *act;
if (!ob->adt || !ob->adt->action)
act = verify_adt_action(id, 1);
else
act = verify_adt_action(id, 0);
if (!ob->adt || !ob->adt->action) {
fprintf(stderr, "Cannot create anim data or action for this object. \n");
return;
}
FCurve *fcu;
std::vector<FCurve*> fcurves = uid_fcurve_map[anim_id];
std::vector<FCurve*>::iterator it;
int i = 0;
for (it = fcurves.begin(); it != fcurves.end(); it++) {
fcu = *it;
strcpy(fcu->rna_path, rna_path);
if (array_index == -1)
fcu->array_index = i;
else
fcu->array_index = array_index;
// convert degrees to radians for rotation
if (strcmp(rna_path, "rotation") == 0) {
for(int j = 0; j < fcu->totvert; j++) {
float rot_intan = fcu->bezt[j].vec[0][1];
float rot_output = fcu->bezt[j].vec[1][1];
float rot_outtan = fcu->bezt[j].vec[2][1];
fcu->bezt[j].vec[0][1] = rot_intan * M_PI / 180.0f;
fcu->bezt[j].vec[1][1] = rot_output * M_PI / 180.0f;
fcu->bezt[j].vec[2][1] = rot_outtan * M_PI / 180.0f;
}
}
i++;
BLI_addtail(&act->curves, fcu);
}
return anim_importer.write_animation(anim);
}
// called on post-process stage after writeVisualScenes
virtual bool writeAnimationList( const COLLADAFW::AnimationList* animationList )
{
const COLLADAFW::UniqueId& anim_list_id = animationList->getUniqueId();
// possible in case we cannot interpret some transform
if (uid_animated_map.find(anim_list_id) == uid_animated_map.end()) {
return true;
}
// what does this AnimationList animate?
AnimatedTransform& animated = uid_animated_map[anim_list_id];
char *loc = "location";
char *rotate = "rotation";
char *scale = "scale";
Object *ob = animated.ob;
const COLLADAFW::AnimationList::AnimationBindings& bindings = animationList->getAnimationBindings();
switch (animated.tm->getTransformationType()) {
case COLLADAFW::Transformation::TRANSLATE:
{
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
switch (binding.animationClass) {
case COLLADAFW::AnimationList::POSITION_X:
change_fcurve(ob, anim_uid, loc, 0);
break;
case COLLADAFW::AnimationList::POSITION_Y:
change_fcurve(ob, anim_uid, loc, 1);
break;
case COLLADAFW::AnimationList::POSITION_Z:
change_fcurve(ob, anim_uid, loc, 2);
break;
case COLLADAFW::AnimationList::POSITION_XYZ:
change_fcurve(ob, anim_uid, loc, -1);
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n", binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::ROTATE:
{
COLLADAFW::Rotate* rot = (COLLADAFW::Rotate*)animated.tm;
COLLADABU::Math::Vector3& axis = rot->getRotationAxis();
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
switch (binding.animationClass) {
case COLLADAFW::AnimationList::ANGLE:
if (COLLADABU::Math::Vector3::UNIT_X == axis) {
change_fcurve(ob, anim_uid, rotate, 0);
}
else if (COLLADABU::Math::Vector3::UNIT_Y == axis) {
change_fcurve(ob, anim_uid, rotate, 1);
}
else if (COLLADABU::Math::Vector3::UNIT_Z == axis) {
change_fcurve(ob, anim_uid, rotate, 2);
}
break;
case COLLADAFW::AnimationList::AXISANGLE:
// convert axis-angle to quat? or XYZ?
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for ROTATE transformation.\n",
binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::SCALE:
{
// same as for TRANSLATE
for (int i = 0; i < bindings.getCount(); i++) {
const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
COLLADAFW::UniqueId anim_uid = binding.animation;
switch (binding.animationClass) {
case COLLADAFW::AnimationList::POSITION_X:
change_fcurve(ob, anim_uid, scale, 0);
break;
case COLLADAFW::AnimationList::POSITION_Y:
change_fcurve(ob, anim_uid, scale, 1);
break;
case COLLADAFW::AnimationList::POSITION_Z:
change_fcurve(ob, anim_uid, scale, 2);
break;
case COLLADAFW::AnimationList::POSITION_XYZ:
change_fcurve(ob, anim_uid, scale, -1);
break;
default:
fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n", binding.animationClass);
}
}
}
break;
case COLLADAFW::Transformation::MATRIX:
case COLLADAFW::Transformation::SKEW:
case COLLADAFW::Transformation::LOOKAT:
fprintf(stderr, "Animation of MATRIX, SKEW and LOOKAT transformations is not supported yet.\n");
break;
}
return true;
return anim_importer.write_animation_list(animationList);
}
/** When this method is called, the writer must write the skin controller data.

View File

@@ -43,15 +43,26 @@ public:
Mat4Transp(out);
}
void mat4_to_dae(double out[][4], float in[][4])
void mat4_to_dae_double(double out[][4], float in[][4])
{
float outf[4][4];
float mat[4][4];
mat4_to_dae(outf, in);
mat4_to_dae(mat, in);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
out[i][j] = outf[i][j];
out[i][j] = mat[i][j];
}
};
class TransformBase
{
protected:
void decompose(float mat[][4], float *loc, float *rot, float *size)
{
Mat4ToSize(mat, size);
Mat4ToEul(mat, rot);
VecCopyf(loc, mat[3]);
}
};