diff options
Diffstat (limited to 'source/blender/collada')
40 files changed, 7461 insertions, 4590 deletions
diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp new file mode 100644 index 00000000000..21cd71a90dc --- /dev/null +++ b/source/blender/collada/AnimationImporter.cpp @@ -0,0 +1,1152 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "DNA_armature_types.h" + +#include "ED_keyframing.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_fcurve.h" +#include "BKE_object.h" + +#include "MEM_guardedalloc.h" + +#include "collada_utils.h" +#include "AnimationImporter.h" +#include "ArmatureImporter.h" + +#include <algorithm> + +// use this for retrieving bone names, since these must be unique +template<class T> +static const char *bc_get_joint_name(T *node) +{ + const std::string& id = node->getOriginalId(); + return id.size() ? id.c_str() : node->getName().c_str(); +} + +FCurve *AnimationImporter::create_fcurve(int array_index, const char *rna_path) +{ + FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve"); + + fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); + fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + fcu->array_index = array_index; + return fcu; +} + +void AnimationImporter::create_bezt(FCurve *fcu, float frame, float output) +{ + BezTriple bez; + memset(&bez, 0, sizeof(BezTriple)); + bez.vec[1][0] = frame; + bez.vec[1][1] = output; + 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, 0); + calchandles_fcurve(fcu); +} + +// create one or several fcurves depending on the number of parameters being animated +void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) +{ + COLLADAFW::FloatOrDoubleArray& input = curve->getInputValues(); + COLLADAFW::FloatOrDoubleArray& output = curve->getOutputValues(); + // COLLADAFW::FloatOrDoubleArray& intan = curve->getInTangentValues(); + // COLLADAFW::FloatOrDoubleArray& outtan = curve->getOutTangentValues(); + float fps = (float)FPS; + size_t dim = curve->getOutDimension(); + unsigned int i; + + std::vector<FCurve*>& fcurves = curve_map[curve->getUniqueId()]; + + switch (dim) { + case 1: // X, Y, Z or angle + case 3: // XYZ + case 16: // matrix + { + for (i = 0; i < dim; i++ ) { + 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 (unsigned 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] = bc_get_float_value(input, j) * fps; + bez.vec[1][1] = bc_get_float_value(output, j * dim + 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, 0); + } + + calchandles_fcurve(fcu); + + fcurves.push_back(fcu); + } + } + break; + default: + fprintf(stderr, "Output dimension of %d is not yet supported (animation id = %s)\n", (int)dim, curve->getOriginalId().c_str()); + } + + for (std::vector<FCurve*>::iterator it = fcurves.begin(); it != fcurves.end(); it++) + unused_curves.push_back(*it); +} + +void AnimationImporter::fcurve_deg_to_rad(FCurve *cu) +{ + for (unsigned int i = 0; i < cu->totvert; i++) { + // TODO convert handles too + cu->bezt[i].vec[1][1] *= M_PI / 180.0f; + } +} + +void AnimationImporter::add_fcurves_to_object(Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index, Animation *animated) +{ + bAction *act; + + if (!ob->adt || !ob->adt->action) act = verify_adt_action((ID*)&ob->id, 1); + else act = ob->adt->action; + + std::vector<FCurve*>::iterator it; + int i; + +#if 0 + char *p = strstr(rna_path, "rotation_euler"); + bool is_rotation = p && *(p + strlen("rotation_euler")) == '\0'; + + // convert degrees to radians for rotation + if (is_rotation) + fcurve_deg_to_rad(fcu); +#endif + + for (it = curves.begin(), i = 0; it != curves.end(); it++, i++) { + FCurve *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; + + if (ob->type == OB_ARMATURE) { + bActionGroup *grp = NULL; + const char *bone_name = bc_get_joint_name(animated->node); + + if (bone_name) { + /* try to find group */ + grp = action_groups_find_named(act, bone_name); + + /* no matching groups, so add one */ + if (grp == NULL) { + /* Add a new group, and make it active */ + grp = (bActionGroup*)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); + + grp->flag = AGRP_SELECTED; + BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); + + BLI_addtail(&act->groups, grp); + BLI_uniquename(&act->groups, grp, "Group", '.', offsetof(bActionGroup, name), 64); + } + + /* add F-Curve to group */ + action_groups_add_channel(act, grp, fcu); + + } +#if 0 + if (is_rotation) { + fcurves_actionGroup_map[grp].push_back(fcu); + } +#endif + } + else { + BLI_addtail(&act->curves, fcu); + } + + // curve is used, so remove it from unused_curves + unused_curves.erase(std::remove(unused_curves.begin(), unused_curves.end(), fcu), unused_curves.end()); + } +} + +AnimationImporter::AnimationImporter(UnitConverter *conv, ArmatureImporter *arm, Scene *scene) : + TransformReader(conv), armature_importer(arm), scene(scene) { } + +AnimationImporter::~AnimationImporter() +{ + // free unused FCurves + for (std::vector<FCurve*>::iterator it = unused_curves.begin(); it != unused_curves.end(); it++) + free_fcurve(*it); + + if (unused_curves.size()) + fprintf(stderr, "removed %d unused curves\n", (int)unused_curves.size()); +} + +bool AnimationImporter::write_animation(const COLLADAFW::Animation* anim) +{ + if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) { + COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim; + + // 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; + } + + // 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: + case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER: + animation_to_fcurves(curve); + break; + default: + // TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types + 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 AnimationImporter::write_animation_list(const COLLADAFW::AnimationList* animlist) +{ + const COLLADAFW::UniqueId& animlist_id = animlist->getUniqueId(); + + animlist_map[animlist_id] = animlist; + +#if 0 + // should not happen + if (uid_animated_map.find(animlist_id) == uid_animated_map.end()) { + return true; + } + + // for bones rna_path is like: pose.bones["bone-name"].rotation + + // what does this AnimationList animate? + Animation& animated = uid_animated_map[animlist_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 = animlist->getAnimationBindings(); + + switch (animated.tm->getTransformationType()) { + case COLLADAFW::Transformation::TRANSLATE: + case COLLADAFW::Transformation::SCALE: + { + bool loc = animated.tm->getTransformationType() == COLLADAFW::Transformation::TRANSLATE; + if (is_joint) + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, loc ? "location" : "scale"); + else + BLI_strncpy(rna_path, loc ? "location" : "scale", 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 (curve_map.find(anim_uid) == curve_map.end()) { + fprintf(stderr, "Cannot find FCurve by animation UID.\n"); + continue; + } + + std::vector<FCurve*>& fcurves = curve_map[anim_uid]; + + switch (binding.animationClass) { + case COLLADAFW::AnimationList::POSITION_X: + add_fcurves_to_object(ob, fcurves, rna_path, 0, &animated); + break; + case COLLADAFW::AnimationList::POSITION_Y: + add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated); + break; + case COLLADAFW::AnimationList::POSITION_Z: + add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated); + break; + case COLLADAFW::AnimationList::POSITION_XYZ: + add_fcurves_to_object(ob, fcurves, rna_path, -1, &animated); + break; + default: + fprintf(stderr, "AnimationClass %d is not supported for %s.\n", + binding.animationClass, loc ? "TRANSLATE" : "SCALE"); + } + } + } + break; + case COLLADAFW::Transformation::ROTATE: + { + if (is_joint) + BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_euler", joint_path); + else + BLI_strncpy(rna_path, "rotation_euler", 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 (curve_map.find(anim_uid) == curve_map.end()) { + fprintf(stderr, "Cannot find FCurve by animation UID.\n"); + continue; + } + + std::vector<FCurve*>& fcurves = curve_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, &animated); + } + else if (COLLADABU::Math::Vector3::UNIT_Y == axis) { + add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated); + } + else if (COLLADABU::Math::Vector3::UNIT_Z == axis) { + add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated); + } + break; + case COLLADAFW::AnimationList::AXISANGLE: + // TODO convert axis-angle to quat? or XYZ? + default: + fprintf(stderr, "AnimationClass %d is not supported for ROTATE 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; + } +#endif + + return true; +} + +void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob) +{ + float mat[4][4]; + TransformReader::get_node_mat(mat, node, &uid_animated_map, ob); + if (ob) { + copy_m4_m4(ob->obmat, mat); + object_apply_mat4(ob, ob->obmat, 0, 0); + } +} + +#if 0 +virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act) +{ + bActionGroup *grp; + int i; + + for (grp = (bActionGroup*)act->groups.first; grp; grp = grp->next) { + + FCurve *eulcu[3] = {NULL, NULL, NULL}; + + if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) + continue; + + std::vector<FCurve*> &rot_fcurves = fcurves_actionGroup_map[grp]; + + if (rot_fcurves.size() > 3) continue; + + for (i = 0; i < rot_fcurves.size(); i++) + eulcu[rot_fcurves[i]->array_index] = rot_fcurves[i]; + + char joint_path[100]; + char rna_path[100]; + + BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name); + BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path); + + FCurve *quatcu[4] = { + create_fcurve(0, rna_path), + create_fcurve(1, rna_path), + create_fcurve(2, rna_path), + create_fcurve(3, rna_path) + }; + + bPoseChannel *chan = get_pose_channel(ob->pose, grp->name); + + float m4[4][4], irest[3][3]; + invert_m4_m4(m4, chan->bone->arm_mat); + copy_m3_m4(irest, m4); + + for (i = 0; i < 3; i++) { + + FCurve *cu = eulcu[i]; + + if (!cu) continue; + + for (int j = 0; j < cu->totvert; j++) { + float frame = cu->bezt[j].vec[1][0]; + + float eul[3] = { + eulcu[0] ? evaluate_fcurve(eulcu[0], frame) : 0.0f, + eulcu[1] ? evaluate_fcurve(eulcu[1], frame) : 0.0f, + eulcu[2] ? evaluate_fcurve(eulcu[2], frame) : 0.0f + }; + + // make eul relative to bone rest pose + float rot[3][3], rel[3][3], quat[4]; + + /*eul_to_mat3(rot, eul); + + mul_m3_m3m3(rel, irest, rot); + + mat3_to_quat(quat, rel); + */ + + eul_to_quat(quat, eul); + + for (int k = 0; k < 4; k++) + create_bezt(quatcu[k], frame, quat[k]); + } + } + + // now replace old Euler curves + + for (i = 0; i < 3; i++) { + if (!eulcu[i]) continue; + + action_groups_remove_channel(act, eulcu[i]); + free_fcurve(eulcu[i]); + } + + chan->rotmode = ROT_MODE_QUAT; + + for (i = 0; i < 4; i++) + action_groups_add_channel(act, grp, quatcu[i]); + } + + bPoseChannel *pchan; + for (pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) { + pchan->rotmode = ROT_MODE_QUAT; + } +} +#endif + +// prerequisites: +// animlist_map - map animlist id -> animlist +// curve_map - map anim id -> curve(s) +Object *AnimationImporter::translate_animation(COLLADAFW::Node *node, + std::map<COLLADAFW::UniqueId, Object*>& object_map, + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& root_map, + COLLADAFW::Transformation::TransformationType tm_type, + Object *par_job) +{ + bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; + bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; + bool is_joint = node->getType() == COLLADAFW::Node::JOINT; + + COLLADAFW::Node *root = root_map.find(node->getUniqueId()) == root_map.end() ? node : root_map[node->getUniqueId()]; + Object *ob = is_joint ? armature_importer->get_armature_for_joint(node) : object_map[node->getUniqueId()]; + const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; + + if (!ob) { + fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); + return NULL; + } + + // frames at which to sample + std::vector<float> frames; + + // for each <rotate>, <translate>, etc. there is a separate Transformation + const COLLADAFW::TransformationPointerArray& tms = node->getTransformations(); + + unsigned int i; + + // find frames at which to sample plus convert all rotation keys to radians + for (i = 0; i < tms.getCount(); i++) { + COLLADAFW::Transformation *tm = tms[i]; + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + + if (type == tm_type) { + const COLLADAFW::UniqueId& listid = tm->getAnimationList(); + + if (animlist_map.find(listid) != animlist_map.end()) { + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings(); + + if (bindings.getCount()) { + for (unsigned int j = 0; j < bindings.getCount(); j++) { + std::vector<FCurve*>& curves = curve_map[bindings[j].animation]; + bool xyz = ((type == COLLADAFW::Transformation::TRANSLATE || type == COLLADAFW::Transformation::SCALE) && bindings[j].animationClass == COLLADAFW::AnimationList::POSITION_XYZ); + + if ((!xyz && curves.size() == 1) || (xyz && curves.size() == 3) || is_matrix) { + std::vector<FCurve*>::iterator iter; + + for (iter = curves.begin(); iter != curves.end(); iter++) { + FCurve *fcu = *iter; + + if (is_rotation) + fcurve_deg_to_rad(fcu); + + for (unsigned int k = 0; k < fcu->totvert; k++) { + float fra = fcu->bezt[k].vec[1][0]; + if (std::find(frames.begin(), frames.end(), fra) == frames.end()) + frames.push_back(fra); + } + } + } + else { + fprintf(stderr, "expected %d curves, got %d\n", xyz ? 3 : 1, (int)curves.size()); + } + } + } + } + } + } + + float irest_dae[4][4]; + float rest[4][4], irest[4][4]; + + if (is_joint) { + get_joint_rest_mat(irest_dae, root, node); + invert_m4(irest_dae); + + Bone *bone = get_named_bone((bArmature*)ob->data, bone_name); + if (!bone) { + fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); + return NULL; + } + + unit_m4(rest); + copy_m4_m4(rest, bone->arm_mat); + invert_m4_m4(irest, rest); + } + + Object *job = NULL; + +#ifdef ARMATURE_TEST + FCurve *job_curves[10]; + job = get_joint_object(root, node, par_job); +#endif + + if (frames.size() == 0) + return job; + + std::sort(frames.begin(), frames.end()); + + const char *tm_str = NULL; + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + tm_str = "rotation_quaternion"; + break; + case COLLADAFW::Transformation::SCALE: + tm_str = "scale"; + break; + case COLLADAFW::Transformation::TRANSLATE: + tm_str = "location"; + break; + case COLLADAFW::Transformation::MATRIX: + break; + default: + return job; + } + + char rna_path[200]; + char joint_path[200]; + + if (is_joint) + armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); + + // new curves + FCurve *newcu[10]; // if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale + unsigned int totcu = is_matrix ? 10 : (is_rotation ? 4 : 3); + + for (i = 0; i < totcu; i++) { + + int axis = i; + + if (is_matrix) { + if (i < 4) { + tm_str = "rotation_quaternion"; + axis = i; + } + else if (i < 7) { + tm_str = "location"; + axis = i - 4; + } + else { + tm_str = "scale"; + axis = i - 7; + } + } + + if (is_joint) + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); + else + strcpy(rna_path, tm_str); + + newcu[i] = create_fcurve(axis, rna_path); + +#ifdef ARMATURE_TEST + if (is_joint) + job_curves[i] = create_fcurve(axis, tm_str); +#endif + } + + std::vector<float>::iterator it; + + // sample values at each frame + for (it = frames.begin(); it != frames.end(); it++) { + float fra = *it; + + float mat[4][4]; + float matfra[4][4]; + + unit_m4(matfra); + + // calc object-space mat + evaluate_transform_at_frame(matfra, node, fra); + + // for joints, we need a special matrix + if (is_joint) { + // special matrix: iR * M * iR_dae * R + // where R, iR are bone rest and inverse rest mats in world space (Blender bones), + // iR_dae is joint inverse rest matrix (DAE) and M is an evaluated joint world-space matrix (DAE) + float temp[4][4], par[4][4]; + + // calc M + calc_joint_parent_mat_rest(par, NULL, root, node); + mul_m4_m4m4(temp, matfra, par); + + // evaluate_joint_world_transform_at_frame(temp, NULL, , node, fra); + + // calc special matrix + mul_serie_m4(mat, irest, temp, irest_dae, rest, NULL, NULL, NULL, NULL); + } + else { + copy_m4_m4(mat, matfra); + } + + float val[4], rot[4], loc[3], scale[3]; + + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + mat4_to_quat(val, mat); + break; + case COLLADAFW::Transformation::SCALE: + mat4_to_size(val, mat); + break; + case COLLADAFW::Transformation::TRANSLATE: + copy_v3_v3(val, mat[3]); + break; + case COLLADAFW::Transformation::MATRIX: + mat4_to_quat(rot, mat); + copy_v3_v3(loc, mat[3]); + mat4_to_size(scale, mat); + break; + default: + break; + } + + // add keys + for (i = 0; i < totcu; i++) { + if (is_matrix) { + if (i < 4) + add_bezt(newcu[i], fra, rot[i]); + else if (i < 7) + add_bezt(newcu[i], fra, loc[i - 4]); + else + add_bezt(newcu[i], fra, scale[i - 7]); + } + else { + add_bezt(newcu[i], fra, val[i]); + } + } + +#ifdef ARMATURE_TEST + if (is_joint) { + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + mat4_to_quat(val, matfra); + break; + case COLLADAFW::Transformation::SCALE: + mat4_to_size(val, matfra); + break; + case COLLADAFW::Transformation::TRANSLATE: + copy_v3_v3(val, matfra[3]); + break; + case MATRIX: + mat4_to_quat(rot, matfra); + copy_v3_v3(loc, matfra[3]); + mat4_to_size(scale, matfra); + break; + default: + break; + } + + for (i = 0; i < totcu; i++) { + if (is_matrix) { + if (i < 4) + add_bezt(job_curves[i], fra, rot[i]); + else if (i < 7) + add_bezt(job_curves[i], fra, loc[i - 4]); + else + add_bezt(job_curves[i], fra, scale[i - 7]); + } + else { + add_bezt(job_curves[i], fra, val[i]); + } + } + } +#endif + } + + verify_adt_action((ID*)&ob->id, 1); + + ListBase *curves = &ob->adt->action->curves; + + // add curves + for (i = 0; i < totcu; i++) { + if (is_joint) + add_bone_fcurve(ob, node, newcu[i]); + else + BLI_addtail(curves, newcu[i]); + +#ifdef ARMATURE_TEST + if (is_joint) + BLI_addtail(&job->adt->action->curves, job_curves[i]); +#endif + } + + if (is_rotation || is_matrix) { + if (is_joint) { + bPoseChannel *chan = get_pose_channel(ob->pose, bone_name); + chan->rotmode = ROT_MODE_QUAT; + } + else { + ob->rotmode = ROT_MODE_QUAT; + } + } + + return job; +} + +// internal, better make it private +// warning: evaluates only rotation +// prerequisites: animlist_map, curve_map +void AnimationImporter::evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra) +{ + const COLLADAFW::TransformationPointerArray& tms = node->getTransformations(); + + unit_m4(mat); + + for (unsigned int i = 0; i < tms.getCount(); i++) { + COLLADAFW::Transformation *tm = tms[i]; + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + float m[4][4]; + + unit_m4(m); + + if (!evaluate_animation(tm, m, fra, node->getOriginalId().c_str())) { + switch (type) { + case COLLADAFW::Transformation::ROTATE: + dae_rotate_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::TRANSLATE: + dae_translate_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::SCALE: + dae_scale_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::MATRIX: + dae_matrix_to_mat4(tm, m); + break; + default: + fprintf(stderr, "unsupported transformation type %d\n", type); + } + } + + float temp[4][4]; + copy_m4_m4(temp, mat); + + mul_m4_m4m4(mat, m, temp); + } +} + +// return true to indicate that mat contains a sane value +bool AnimationImporter::evaluate_animation(COLLADAFW::Transformation *tm, float mat[4][4], float fra, const char *node_id) +{ + const COLLADAFW::UniqueId& listid = tm->getAnimationList(); + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + + if (type != COLLADAFW::Transformation::ROTATE && + type != COLLADAFW::Transformation::SCALE && + type != COLLADAFW::Transformation::TRANSLATE && + type != COLLADAFW::Transformation::MATRIX) { + fprintf(stderr, "animation of transformation %d is not supported yet\n", type); + return false; + } + + if (animlist_map.find(listid) == animlist_map.end()) + return false; + + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings(); + + if (bindings.getCount()) { + float vec[3]; + + bool is_scale = (type == COLLADAFW::Transformation::SCALE); + bool is_translate = (type == COLLADAFW::Transformation::TRANSLATE); + + if (type == COLLADAFW::Transformation::SCALE) + dae_scale_to_v3(tm, vec); + else if (type == COLLADAFW::Transformation::TRANSLATE) + dae_translate_to_v3(tm, vec); + + for (unsigned int j = 0; j < bindings.getCount(); j++) { + const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[j]; + std::vector<FCurve*>& curves = curve_map[binding.animation]; + COLLADAFW::AnimationList::AnimationClass animclass = binding.animationClass; + char path[100]; + + switch (type) { + case COLLADAFW::Transformation::ROTATE: + BLI_snprintf(path, sizeof(path), "%s.rotate (binding %u)", node_id, j); + break; + case COLLADAFW::Transformation::SCALE: + BLI_snprintf(path, sizeof(path), "%s.scale (binding %u)", node_id, j); + break; + case COLLADAFW::Transformation::TRANSLATE: + BLI_snprintf(path, sizeof(path), "%s.translate (binding %u)", node_id, j); + break; + case COLLADAFW::Transformation::MATRIX: + BLI_snprintf(path, sizeof(path), "%s.matrix (binding %u)", node_id, j); + break; + default: + break; + } + + if (animclass == COLLADAFW::AnimationList::UNKNOWN_CLASS) { + fprintf(stderr, "%s: UNKNOWN animation class\n", path); + continue; + } + + if (type == COLLADAFW::Transformation::ROTATE) { + if (curves.size() != 1) { + fprintf(stderr, "expected 1 curve, got %d\n", (int)curves.size()); + return false; + } + + // TODO support other animclasses + if (animclass != COLLADAFW::AnimationList::ANGLE) { + fprintf(stderr, "%s: animation class %d is not supported yet\n", path, animclass); + return false; + } + + COLLADABU::Math::Vector3& axis = ((COLLADAFW::Rotate*)tm)->getRotationAxis(); + float ax[3] = {axis[0], axis[1], axis[2]}; + float angle = evaluate_fcurve(curves[0], fra); + axis_angle_to_mat4(mat, ax, angle); + + return true; + } + else if (is_scale || is_translate) { + bool is_xyz = animclass == COLLADAFW::AnimationList::POSITION_XYZ; + + if ((!is_xyz && curves.size() != 1) || (is_xyz && curves.size() != 3)) { + if (is_xyz) + fprintf(stderr, "%s: expected 3 curves, got %d\n", path, (int)curves.size()); + else + fprintf(stderr, "%s: expected 1 curve, got %d\n", path, (int)curves.size()); + return false; + } + + switch (animclass) { + case COLLADAFW::AnimationList::POSITION_X: + vec[0] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_Y: + vec[1] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_Z: + vec[2] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_XYZ: + vec[0] = evaluate_fcurve(curves[0], fra); + vec[1] = evaluate_fcurve(curves[1], fra); + vec[2] = evaluate_fcurve(curves[2], fra); + break; + default: + fprintf(stderr, "%s: animation class %d is not supported yet\n", path, animclass); + break; + } + } + else if (type == COLLADAFW::Transformation::MATRIX) { + // for now, of matrix animation, support only the case when all values are packed into one animation + if (curves.size() != 16) { + fprintf(stderr, "%s: expected 16 curves, got %d\n", path, (int)curves.size()); + return false; + } + + COLLADABU::Math::Matrix4 matrix; + int i = 0, j = 0; + + for (std::vector<FCurve*>::iterator it = curves.begin(); it != curves.end(); it++) { + matrix.setElement(i, j, evaluate_fcurve(*it, fra)); + j++; + if (j == 4) { + i++; + j = 0; + } + } + + COLLADAFW::Matrix tm(matrix); + dae_matrix_to_mat4(&tm, mat); + + return true; + } + } + + if (is_scale) + size_to_mat4(mat, vec); + else + copy_v3_v3(mat[3], vec); + + return is_scale || is_translate; + } + + return false; +} + +// gives a world-space mat of joint at rest position +void AnimationImporter::get_joint_rest_mat(float mat[4][4], COLLADAFW::Node *root, COLLADAFW::Node *node) +{ + // if bind mat is not available, + // use "current" node transform, i.e. all those tms listed inside <node> + if (!armature_importer->get_joint_bind_mat(mat, node)) { + float par[4][4], m[4][4]; + + calc_joint_parent_mat_rest(par, NULL, root, node); + get_node_mat(m, node, NULL, NULL); + mul_m4_m4m4(mat, m, par); + } +} + +// gives a world-space mat, end's mat not included +bool AnimationImporter::calc_joint_parent_mat_rest(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end) +{ + float m[4][4]; + + if (node == end) { + par ? copy_m4_m4(mat, par) : unit_m4(mat); + return true; + } + + // use bind matrix if available or calc "current" world mat + if (!armature_importer->get_joint_bind_mat(m, node)) { + if (par) { + float temp[4][4]; + get_node_mat(temp, node, NULL, NULL); + mul_m4_m4m4(m, temp, par); + } + else { + get_node_mat(m, node, NULL, NULL); + } + } + + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (calc_joint_parent_mat_rest(mat, m, children[i], end)) + return true; + } + + return false; +} + +#ifdef ARMATURE_TEST +Object *AnimationImporter::get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job) +{ + if (joint_objects.find(node->getUniqueId()) == joint_objects.end()) { + Object *job = add_object(scene, OB_EMPTY); + + rename_id((ID*)&job->id, (char*)get_joint_name(node)); + + job->lay = object_in_scene(job, scene)->lay = 2; + + mul_v3_fl(job->size, 0.5f); + job->recalc |= OB_RECALC_OB; + + verify_adt_action((ID*)&job->id, 1); + + job->rotmode = ROT_MODE_QUAT; + + float mat[4][4]; + get_joint_rest_mat(mat, root, node); + + if (par_job) { + float temp[4][4], ipar[4][4]; + invert_m4_m4(ipar, par_job->obmat); + copy_m4_m4(temp, mat); + mul_m4_m4m4(mat, temp, ipar); + } + + TransformBase::decompose(mat, job->loc, NULL, job->quat, job->size); + + if (par_job) { + job->parent = par_job; + + par_job->recalc |= OB_RECALC_OB; + job->parsubstr[0] = 0; + } + + where_is_object(scene, job); + + // after parenting and layer change + DAG_scene_sort(CTX_data_main(C), scene); + + joint_objects[node->getUniqueId()] = job; + } + + return joint_objects[node->getUniqueId()]; +} +#endif + +#if 0 +// recursively evaluates joint tree until end is found, mat then is world-space matrix of end +// mat must be identity on enter, node must be root +bool AnimationImporter::evaluate_joint_world_transform_at_frame(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra) +{ + float m[4][4]; + if (par) { + float temp[4][4]; + evaluate_transform_at_frame(temp, node, node == end ? fra : 0.0f); + mul_m4_m4m4(m, temp, par); + } + else { + evaluate_transform_at_frame(m, node, node == end ? fra : 0.0f); + } + + if (node == end) { + copy_m4_m4(mat, m); + return true; + } + else { + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + if (evaluate_joint_world_transform_at_frame(mat, m, children[i], end, fra)) + return true; + } + } + + return false; +} +#endif + +void AnimationImporter::add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu) +{ + const char *bone_name = bc_get_joint_name(node); + bAction *act = ob->adt->action; + + /* try to find group */ + bActionGroup *grp = action_groups_find_named(act, bone_name); + + /* no matching groups, so add one */ + if (grp == NULL) { + /* Add a new group, and make it active */ + grp = (bActionGroup*)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); + + grp->flag = AGRP_SELECTED; + BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); + + BLI_addtail(&act->groups, grp); + BLI_uniquename(&act->groups, grp, "Group", '.', offsetof(bActionGroup, name), 64); + } + + /* add F-Curve to group */ + action_groups_add_channel(act, grp, fcu); +} + +void AnimationImporter::add_bezt(FCurve *fcu, float fra, float value) +{ + BezTriple bez; + memset(&bez, 0, sizeof(BezTriple)); + bez.vec[1][0] = fra; + bez.vec[1][1] = value; + 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, 0); + calchandles_fcurve(fcu); +} diff --git a/source/blender/collada/AnimationImporter.h b/source/blender/collada/AnimationImporter.h new file mode 100644 index 00000000000..05347a1fbc1 --- /dev/null +++ b/source/blender/collada/AnimationImporter.h @@ -0,0 +1,136 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file AnimationImporter.h + * \ingroup collada + */ + +#ifndef __BC_ANIMATIONIMPORTER_H__ +#define __BC_ANIMATIONIMPORTER_H__ + +#include <map> +#include <vector> + +#include "COLLADAFWAnimation.h" +#include "COLLADAFWAnimationCurve.h" +#include "COLLADAFWAnimationList.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWUniqueId.h" + +#include "DNA_anim_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +//#include "ArmatureImporter.h" +#include "TransformReader.h" + +#include "collada_internal.h" + +class ArmatureImporter; + +class AnimationImporterBase +{ +public: + // virtual void change_eul_to_quat(Object *ob, bAction *act) = 0; +}; + +class AnimationImporter : private TransformReader, public AnimationImporterBase +{ +private: + + ArmatureImporter *armature_importer; + Scene *scene; + + std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > curve_map; + std::map<COLLADAFW::UniqueId, TransformReader::Animation> uid_animated_map; + // std::map<bActionGroup*, std::vector<FCurve*> > fcurves_actionGroup_map; + std::map<COLLADAFW::UniqueId, const COLLADAFW::AnimationList*> animlist_map; + std::vector<FCurve*> unused_curves; + std::map<COLLADAFW::UniqueId, Object*> joint_objects; + + FCurve *create_fcurve(int array_index, const char *rna_path); + + void create_bezt(FCurve *fcu, float frame, float output); + + // create one or several fcurves depending on the number of parameters being animated + void animation_to_fcurves(COLLADAFW::AnimationCurve *curve); + + void fcurve_deg_to_rad(FCurve *cu); + + void add_fcurves_to_object(Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index, Animation *animated); +public: + + AnimationImporter(UnitConverter *conv, ArmatureImporter *arm, Scene *scene); + + ~AnimationImporter(); + + bool write_animation(const COLLADAFW::Animation* anim); + + // called on post-process stage after writeVisualScenes + bool write_animation_list(const COLLADAFW::AnimationList* animlist); + + void read_node_transform(COLLADAFW::Node *node, Object *ob); +#if 0 + virtual void change_eul_to_quat(Object *ob, bAction *act); +#endif + + // prerequisites: + // animlist_map - map animlist id -> animlist + // curve_map - map anim id -> curve(s) + Object *translate_animation(COLLADAFW::Node *node, + std::map<COLLADAFW::UniqueId, Object*>& object_map, + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& root_map, + COLLADAFW::Transformation::TransformationType tm_type, + Object *par_job = NULL); + + // internal, better make it private + // warning: evaluates only rotation + // prerequisites: animlist_map, curve_map + void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra); + + // return true to indicate that mat contains a sane value + bool evaluate_animation(COLLADAFW::Transformation *tm, float mat[4][4], float fra, const char *node_id); + + // gives a world-space mat of joint at rest position + void get_joint_rest_mat(float mat[4][4], COLLADAFW::Node *root, COLLADAFW::Node *node); + + // gives a world-space mat, end's mat not included + bool calc_joint_parent_mat_rest(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end); + +#ifdef ARMATURE_TEST + Object *get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job); +#endif + +#if 0 + // recursively evaluates joint tree until end is found, mat then is world-space matrix of end + // mat must be identity on enter, node must be root + bool evaluate_joint_world_transform_at_frame(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra); +#endif + + void add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu); + + void add_bezt(FCurve *fcu, float fra, float value); +}; + + #endif diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp new file mode 100644 index 00000000000..e8335173d50 --- /dev/null +++ b/source/blender/collada/ArmatureExporter.cpp @@ -0,0 +1,469 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "COLLADASWBaseInputElement.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWSource.h" + +#include "DNA_action_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" + +#include "BLI_listbase.h" + +#include "GeometryExporter.h" +#include "ArmatureExporter.h" + +// 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? +ArmatureExporter::ArmatureExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryControllers(sw) {} + +// write bone nodes +void ArmatureExporter::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 ArmatureExporter::is_skinned_mesh(Object *ob) +{ + return get_assigned_armature(ob) != NULL; +} + +void ArmatureExporter::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, ob); + + 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 ArmatureExporter::export_controllers(Scene *sce) +{ + scene = sce; + + openLibrary(); + + GeometryFunctor gf; + gf.forEachMeshObjectInScene<ArmatureExporter>(sce, *this); + + closeLibrary(); +} + +void ArmatureExporter::operator()(Object *ob) +{ + Object *ob_arm = get_assigned_armature(ob); + + if (ob_arm /*&& !already_written(ob_arm)*/) + export_controller(ob, ob_arm); +} +#if 0 + +bool ArmatureExporter::already_written(Object *ob_arm) +{ + return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != written_armatures.end(); +} + +void ArmatureExporter::wrote(Object *ob_arm) +{ + written_armatures.push_back(ob_arm); +} + +void ArmatureExporter::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 *ArmatureExporter::get_assigned_armature(Object *ob) +{ + Object *ob_arm = NULL; + + if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { + ob_arm = ob->parent; + } + else { + ModifierData *mod = (ModifierData*)ob->modifiers.first; + while (mod) { + if (mod->type == eModifierType_Armature) { + ob_arm = ((ArmatureModifierData*)mod)->object; + } + + mod = mod->next; + } + } + + return ob_arm; +} + +std::string ArmatureExporter::get_joint_sid(Bone *bone, Object *ob_arm) +{ + return get_joint_id(bone, ob_arm); +} + +// parent_mat is armature-space +void ArmatureExporter::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, ob_arm); + + 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 ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node) +{ + 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]; + invert_m4_m4(invpar, parchan->pose_mat); + mul_m4_m4m4(mat, pchan->pose_mat, invpar); + } + else { + // get world-space from armature-space + mul_m4_m4m4(mat, pchan->pose_mat, ob_arm->obmat); + } + + TransformWriter::add_node_transform(node, mat, NULL); +} + +std::string ArmatureExporter::get_controller_id(Object *ob_arm, Object *ob) +{ + return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + SKIN_CONTROLLER_ID_SUFFIX; +} + +// ob should be of type OB_MESH +// both args are required +void ArmatureExporter::export_controller(Object* ob, Object *ob_arm) +{ + // joint names + // joint inverse bind matrices + // vertex weights + + // input: + // joint names: ob -> vertex group names + // vertex group weights: me->dvert -> groups -> index, weight + + /* + me->dvert: + + typedef struct MDeformVert { + struct MDeformWeight *dw; + int totweight; + int flag; // flag only in use for weightpaint now + } MDeformVert; + + typedef struct MDeformWeight { + int def_nr; + float weight; + } MDeformWeight; + */ + + Mesh *me = (Mesh*)ob->data; + if (!me->dvert) return; + + std::string controller_name = id_name(ob_arm); + std::string controller_id = get_controller_id(ob_arm, ob); + + 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_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, ob_arm, &ob->defbase); + + closeSkin(); + closeController(); +} + +void ArmatureExporter::add_joints_element(ListBase *defbase, + const std::string& joints_source_id, const std::string& inv_bind_mat_source_id) +{ + COLLADASW::JointsElement joints(mSW); + COLLADASW::InputList &input = joints.getInputList(); + + input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id))); + input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::BINDMATRIX, + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id))); + joints.add(); +} + +void ArmatureExporter::add_bind_shape_mat(Object *ob) +{ + double bind_mat[4][4]; + + converter.mat4_to_dae_double(bind_mat, ob->obmat); + + addBindShapeTransform(bind_mat); +} + +std::string ArmatureExporter::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(totjoint); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("JOINT"); + + source.prepareToAppendValues(); + + for (def = (bDeformGroup*)defbase->first; def; def = def->next) { + Bone *bone = get_bone_from_defgroup(ob_arm, def); + if (bone) + source.appendValues(get_joint_sid(bone, ob_arm)); + } + + source.finish(); + + return source_id; +} + +std::string ArmatureExporter::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; + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(BLI_countlist(defbase)); + source.setAccessorStride(16); + + source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("TRANSFORM"); + + source.prepareToAppendValues(); + + bPose *pose = ob_arm->pose; + bArmature *arm = (bArmature*)ob_arm->data; + + int flag = arm->flag; + + // put armature in rest position + if (!(arm->flag & ARM_RESTPOS)) { + arm->flag |= ARM_RESTPOS; + where_is_pose(scene, ob_arm); + } + + for (bDeformGroup *def = (bDeformGroup*)defbase->first; def; def = def->next) { + if (is_bone_defgroup(ob_arm, def)) { + + 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 + mul_m4_m4m4(world, pchan->pose_mat, ob_arm->obmat); + + invert_m4_m4(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(); + + return source_id; +} + +Bone *ArmatureExporter::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 ArmatureExporter::is_bone_defgroup(Object *ob_arm, bDeformGroup* def) +{ + return get_bone_from_defgroup(ob_arm, def) != NULL; +} + +std::string ArmatureExporter::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(totweight); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("WEIGHT"); + + source.prepareToAppendValues(); + + // NOTE: COLLADA spec says weights should be normalized + + 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); + } + } + + source.finish(); + + return source_id; +} + +void ArmatureExporter::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(); + + int offset = 0; + input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), offset++)); + input.push_back(COLLADASW::Input(COLLADASW::InputSemantic::WEIGHT, + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), offset++)); + + weights.setCount(me->totvert); + + // write number of deformers per vertex + COLLADASW::PrimitivesBase::VCountList vcount; + int i; + for (i = 0; i < me->totvert; i++) { + vcount.push_back(me->dvert[i].totweight); + } + + weights.prepareToAppendVCountValues(); + weights.appendVertexCount(vcount); + + // 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++) { + weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]); + weights.appendValues(weight_index++); + } + } + + weights.finish(); +} diff --git a/source/blender/collada/ArmatureExporter.h b/source/blender/collada/ArmatureExporter.h new file mode 100644 index 00000000000..f72e5244a36 --- /dev/null +++ b/source/blender/collada/ArmatureExporter.h @@ -0,0 +1,141 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ArmatureExporter.h + * \ingroup collada + */ + +#ifndef __ARMATUREEXPORTER_H__ +#define __ARMATUREEXPORTER_H__ + +#include <string> +//#include <vector> + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryControllers.h" +#include "COLLADASWInputList.h" +#include "COLLADASWNode.h" + +#include "DNA_armature_types.h" +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "TransformWriter.h" +#include "InstanceWriter.h" + +// 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); + + // write bone nodes + void add_armature_bones(Object *ob_arm, Scene *sce); + + bool is_skinned_mesh(Object *ob); + + void add_instance_controller(Object *ob); + + void export_controllers(Scene *sce); + + void operator()(Object *ob); + +private: + + UnitConverter converter; + +#if 0 + std::vector<Object*> written_armatures; + + bool already_written(Object *ob_arm); + + void wrote(Object *ob_arm); + + void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce); +#endif + + Object *get_assigned_armature(Object *ob); + + std::string get_joint_sid(Bone *bone, Object *ob_arm); + + // parent_mat is armature-space + void add_bone_node(Bone *bone, Object *ob_arm); + + void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node); + + std::string get_controller_id(Object *ob_arm, Object *ob); + + // ob should be of type OB_MESH + // both args are required + void export_controller(Object* ob, Object *ob_arm); + + void add_joints_element(ListBase *defbase, + const std::string& joints_source_id, const std::string& inv_bind_mat_source_id); + + void add_bind_shape_mat(Object *ob); + + std::string add_joints_source(Object *ob_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); + + Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup* def); + + bool is_bone_defgroup(Object *ob_arm, bDeformGroup* def); + + std::string add_weights_source(Mesh *me, const std::string& controller_id); + + void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me, + Object *ob_arm, ListBase *defbase); +}; + +/* +struct GeometryFunctor { + // f should have + // void operator()(Object* ob) + template<class Functor> + void forEachMeshObjectInScene(Scene *sce, Functor &f) + { + + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + if (ob->type == OB_MESH && ob->data) { + f(ob); + } + base= base->next; + + } + } +};*/ + +#endif diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp new file mode 100644 index 00000000000..bf51e64b0bd --- /dev/null +++ b/source/blender/collada/ArmatureImporter.cpp @@ -0,0 +1,587 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include <algorithm> + +#include "COLLADAFWUniqueId.h" + +#include "BKE_action.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" +#include "BLI_string.h" +#include "ED_armature.h" + +#include "ArmatureImporter.h" + +// use this for retrieving bone names, since these must be unique +template<class T> +static const char *bc_get_joint_name(T *node) +{ + const std::string& id = node->getOriginalId(); + return id.size() ? id.c_str() : node->getName().c_str(); +} + +ArmatureImporter::ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce) : + TransformReader(conv), scene(sce), empty(NULL), mesh_importer(mesh), anim_importer(anim) {} + +ArmatureImporter::~ArmatureImporter() +{ + // free skin controller data if we forget to do this earlier + std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + it->second.free(); + } +} + +#if 0 +JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); +{ + const COLLADAFW::UniqueId& joint_id = node->getUniqueId(); + + if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) { + fprintf(stderr, "Cannot find a joint index by joint id for %s.\n", + node->getOriginalId().c_str()); + return NULL; + } + + int joint_index = joint_id_to_joint_index_map[joint_id]; + + return &joint_index_to_joint_info_map[joint_index]; +} +#endif + +void ArmatureImporter::create_bone(SkinInfo& skin, COLLADAFW::Node *node, EditBone *parent, int totchild, + float parent_mat[][4], bArmature *arm) +{ + float joint_inv_bind_mat[4][4]; + + // JointData* jd = get_joint_data(node); + + float mat[4][4]; + + if (skin.get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { + // get original world-space matrix + invert_m4_m4(mat, joint_inv_bind_mat); + } + // create a bone even if there's no joint data for it (i.e. it has no influence) + else { + float obmat[4][4]; + + // object-space + get_node_mat(obmat, node, NULL, NULL); + + // get world-space + if (parent) + mul_m4_m4m4(mat, obmat, parent_mat); + else + copy_m4_m4(mat, obmat); + } + + // TODO rename from Node "name" attrs later + EditBone *bone = ED_armature_edit_bone_add(arm, (char*)bc_get_joint_name(node)); + totbone++; + + if (parent) bone->parent = parent; + + // set head + copy_v3_v3(bone->head, mat[3]); + + // set tail, don't set it to head because 0-length bones are not allowed + float vec[3] = {0.0f, 0.5f, 0.0f}; + add_v3_v3v3(bone->tail, bone->head, vec); + + // set parent tail + if (parent && totchild == 1) { + copy_v3_v3(parent->tail, bone->head); + + // not setting BONE_CONNECTED because this would lock child bone location with respect to parent + // bone->flag |= BONE_CONNECTED; + + // XXX increase this to prevent "very" small bones? + const float epsilon = 0.000001f; + + // derive leaf bone length + float length = len_v3v3(parent->head, parent->tail); + if ((length < leaf_bone_length || totbone == 0) && length > epsilon) { + leaf_bone_length = length; + } + + // treat zero-sized bone like a leaf bone + if (length <= epsilon) { + add_leaf_bone(parent_mat, parent); + } + + /* +#if 0 + // and which row in mat is bone direction + float vec[3]; + sub_v3_v3v3(vec, parent->tail, parent->head); +#ifdef COLLADA_DEBUG + print_v3("tail - head", vec); + print_m4("matrix", parent_mat); +#endif + for (int i = 0; i < 3; i++) { +#ifdef COLLADA_DEBUG + char *axis_names[] = {"X", "Y", "Z"}; + printf("%s-axis length is %f\n", axis_names[i], len_v3(parent_mat[i])); +#endif + float angle = angle_v2v2(vec, parent_mat[i]); + if (angle < min_angle) { +#ifdef COLLADA_DEBUG + print_v3("picking", parent_mat[i]); + printf("^ %s axis of %s's matrix\n", axis_names[i], get_dae_name(node)); +#endif + bone_direction_row = i; + min_angle = angle; + } + } +#endif + */ + } + + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + create_bone(skin, children[i], bone, children.getCount(), mat, arm); + } + + // in second case it's not a leaf bone, but we handle it the same way + if (!children.getCount() || children.getCount() > 1) { + add_leaf_bone(mat, bone); + } +} + +void ArmatureImporter::add_leaf_bone(float mat[][4], EditBone *bone) +{ + LeafBone leaf; + + leaf.bone = bone; + copy_m4_m4(leaf.mat, mat); + BLI_strncpy(leaf.name, bone->name, sizeof(leaf.name)); + + leaf_bones.push_back(leaf); +} + +void ArmatureImporter::fix_leaf_bones() +{ + // just setting tail for leaf bones here + + std::vector<LeafBone>::iterator it; + for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { + LeafBone& leaf = *it; + + // pointing up + float vec[3] = {0.0f, 0.0f, 1.0f}; + + mul_v3_fl(vec, leaf_bone_length); + + copy_v3_v3(leaf.bone->tail, leaf.bone->head); + add_v3_v3v3(leaf.bone->tail, leaf.bone->head, vec); + } +} + +#if 0 +void ArmatureImporter::set_leaf_bone_shapes(Object *ob_arm) +{ + bPose *pose = ob_arm->pose; + + std::vector<LeafBone>::iterator it; + for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { + LeafBone& leaf = *it; + + bPoseChannel *pchan = get_pose_channel(pose, leaf.name); + if (pchan) { + pchan->custom = get_empty_for_leaves(); + } + else { + fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name); + } + } +} + +void ArmatureImporter::set_euler_rotmode() +{ + // just set rotmode = ROT_MODE_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_or_descendant(joint)) { + bPoseChannel *pchan = skin.get_pose_channel_from_node(joint); + + if (pchan) { + pchan->rotmode = ROT_MODE_EUL; + } + else { + fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint)); + } + + break; + } + } + } +} +#endif + +Object *ArmatureImporter::get_empty_for_leaves() +{ + if (empty) return empty; + + empty = add_object(scene, OB_EMPTY); + empty->empty_drawtype = OB_EMPTY_SPHERE; + + return empty; +} + +#if 0 +Object *ArmatureImporter::find_armature(COLLADAFW::Node *node) +{ + JointData* jd = get_joint_data(node); + if (jd) return jd->ob_arm; + + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + Object *ob_arm = find_armature(children[i]); + if (ob_arm) return ob_arm; + } + + return NULL; +} + +ArmatureJoints& ArmatureImporter::get_armature_joints(Object *ob_arm) +{ + // try finding it + std::vector<ArmatureJoints>::iterator it; + for (it = armature_joints.begin(); it != armature_joints.end(); it++) { + if ((*it).ob_arm == ob_arm) return *it; + } + + // not found, create one + ArmatureJoints aj; + aj.ob_arm = ob_arm; + armature_joints.push_back(aj); + + return armature_joints.back(); +} +#endif + +void ArmatureImporter::create_armature_bones(SkinInfo& skin) +{ + // just do like so: + // - get armature + // - enter editmode + // - add edit bones and head/tail properties using matrices and parent-child info + // - exit edit mode + // - set a sphere shape to leaf bones + + Object *ob_arm = NULL; + + /* + * find if there's another skin sharing at least one bone with this skin + * if so, use that skin's armature + */ + + /* + Pseudocode: + + find_node_in_tree(node, root_joint) + + skin::find_root_joints(root_joints): + std::vector root_joints; + for each root in root_joints: + for each joint in joints: + if find_node_in_tree(joint, root): + if (std::find(root_joints.begin(), root_joints.end(), root) == root_joints.end()) + root_joints.push_back(root); + + for (each skin B with armature) { + find all root joints for skin B + + for each joint X in skin A: + for each root joint R in skin B: + if (find_node_in_tree(X, R)) { + shared = 1; + goto endloop; + } + } + + endloop: + */ + + SkinInfo *a = &skin; + Object *shared = NULL; + std::vector<COLLADAFW::Node*> skin_root_joints; + + std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + SkinInfo *b = &it->second; + if (b == a || b->get_armature() == NULL) + continue; + + skin_root_joints.clear(); + + b->find_root_joints(root_joints, joint_by_uid, skin_root_joints); + + std::vector<COLLADAFW::Node*>::iterator ri; + for (ri = skin_root_joints.begin(); ri != skin_root_joints.end(); ri++) { + if (a->uses_joint_or_descendant(*ri)) { + shared = b->get_armature(); + break; + } + } + + if (shared != NULL) + break; + } + + if (shared) + ob_arm = skin.set_armature(shared); + else + ob_arm = skin.create_armature(scene); + + // enter armature edit mode + ED_armature_to_edit(ob_arm); + + leaf_bones.clear(); + totbone = 0; + // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on default row + leaf_bone_length = FLT_MAX; + // min_angle = 360.0f; // minimum angle between bone head-tail and a row of bone matrix + + // create bones + /* + TODO: + check if bones have already been created for a given joint + */ + + std::vector<COLLADAFW::Node*>::iterator ri; + for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { + // for shared armature check if bone tree is already created + if (shared && std::find(skin_root_joints.begin(), skin_root_joints.end(), *ri) != skin_root_joints.end()) + continue; + + // since root_joints may contain joints for multiple controllers, we need to filter + if (skin.uses_joint_or_descendant(*ri)) { + create_bone(skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, (bArmature*)ob_arm->data); + + if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) + skin.set_parent(joint_parent_map[(*ri)->getUniqueId()]); + } + } + + fix_leaf_bones(); + + // exit armature edit mode + ED_armature_from_edit(ob_arm); + ED_armature_edit_free(ob_arm); + DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB|OB_RECALC_DATA); + + // set_leaf_bone_shapes(ob_arm); + // set_euler_rotmode(); +} + + +// root - if this joint is the top joint in hierarchy, if a joint +// is a child of a node (not joint), root should be true since +// this is where we build armature bones from +void ArmatureImporter::add_joint(COLLADAFW::Node *node, bool root, Object *parent) +{ + joint_by_uid[node->getUniqueId()] = node; + if (root) { + root_joints.push_back(node); + + if (parent) + joint_parent_map[node->getUniqueId()] = parent; + } +} + +#if 0 +void ArmatureImporter::add_root_joint(COLLADAFW::Node *node) +{ + // root_joints.push_back(node); + Object *ob_arm = find_armature(node); + if (ob_arm) { + get_armature_joints(ob_arm).root_joints.push_back(node); + } +#ifdef COLLADA_DEBUG + else { + fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node)); + } +#endif +} +#endif + +// here we add bones to armatures, having armatures previously created in write_controller +void ArmatureImporter::make_armatures(bContext *C) +{ + 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; + + create_armature_bones(skin); + + // link armature with a mesh object + Object *ob = mesh_importer->get_object_by_geom_uid(*get_geometry_uid(skin.get_controller_uid())); + if (ob) + skin.link_armature(C, ob, joint_by_uid, this); + else + fprintf(stderr, "Cannot find object to link armature with.\n"); + + // set armature parent if any + Object *par = skin.get_parent(); + if (par) + bc_set_parent(skin.get_armature(), par, C, false); + + // free memory stolen from SkinControllerData + skin.free(); + } +} + +#if 0 +// link with meshes, create vertex groups, assign weights +void ArmatureImporter::link_armature(Object *ob_arm, const COLLADAFW::UniqueId& geom_id, const COLLADAFW::UniqueId& controller_data_id) +{ + Object *ob = mesh_importer->get_object_by_geom_uid(geom_id); + + if (!ob) { + fprintf(stderr, "Cannot find object by geometry UID.\n"); + return; + } + + if (skin_by_data_uid.find(controller_data_id) == skin_by_data_uid.end()) { + fprintf(stderr, "Cannot find skin info by controller data UID.\n"); + return; + } + + SkinInfo& skin = skin_by_data_uid[conroller_data_id]; + + // create vertex groups +} +#endif + +bool ArmatureImporter::write_skin_controller_data(const COLLADAFW::SkinControllerData* data) +{ + // at this stage we get vertex influence info that should go into me->verts and ob->defbase + // there's no info to which object this should be long so we associate it with skin controller data UID + + // don't forget to call defgroup_unique_name before we copy + + // controller data uid -> [armature] -> joint data, + // [mesh object] + // + + SkinInfo skin(unit_converter); + skin.borrow_skin_controller_data(data); + + // store join inv bind matrix to use it later in armature construction + const COLLADAFW::Matrix4Array& inv_bind_mats = data->getInverseBindMatrices(); + for (unsigned int i = 0; i < data->getJointsCount(); i++) { + skin.add_joint(inv_bind_mats[i]); + } + + skin_by_data_uid[data->getUniqueId()] = skin; + + return true; +} + +bool ArmatureImporter::write_controller(const COLLADAFW::Controller* controller) +{ + // - create and store armature object + + const COLLADAFW::UniqueId& skin_id = controller->getUniqueId(); + + if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) { + COLLADAFW::SkinController *co = (COLLADAFW::SkinController*)controller; + // to be able to find geom id by controller id + geom_uid_by_controller_uid[skin_id] = co->getSource(); + + const COLLADAFW::UniqueId& data_uid = co->getSkinControllerData(); + if (skin_by_data_uid.find(data_uid) == skin_by_data_uid.end()) { + fprintf(stderr, "Cannot find skin by controller data UID.\n"); + return true; + } + + skin_by_data_uid[data_uid].set_controller(co); + } + // morph controller + else { + // shape keys? :) + fprintf(stderr, "Morph controller is not supported yet.\n"); + } + + return true; +} + +COLLADAFW::UniqueId *ArmatureImporter::get_geometry_uid(const COLLADAFW::UniqueId& controller_uid) +{ + if (geom_uid_by_controller_uid.find(controller_uid) == geom_uid_by_controller_uid.end()) + return NULL; + + return &geom_uid_by_controller_uid[controller_uid]; +} + +Object *ArmatureImporter::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_or_descendant(node)) + return skin.get_armature(); + } + + return NULL; +} + +void ArmatureImporter::get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count) +{ + BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bc_get_joint_name(node)); +} + +// gives a world-space mat +bool ArmatureImporter::get_joint_bind_mat(float m[][4], COLLADAFW::Node *joint) +{ + std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; + bool found = false; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + SkinInfo& skin = it->second; + if ((found = skin.get_joint_inv_bind_matrix(m, joint))) { + invert_m4(m); + break; + } + } + + return found; +} diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h new file mode 100644 index 00000000000..d36bccf7e57 --- /dev/null +++ b/source/blender/collada/ArmatureImporter.h @@ -0,0 +1,163 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ArmatureImporter.h + * \ingroup collada + */ + +#ifndef __BC_ARMATUREIMPORTER_H__ +#define __BC_ARMATUREIMPORTER_H__ + +#include "COLLADAFWNode.h" +#include "COLLADAFWUniqueId.h" + +extern "C" { +#include "BKE_context.h" + +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "ED_armature.h" +} + +#include "AnimationImporter.h" +#include "MeshImporter.h" +#include "SkinInfo.h" +#include "TransformReader.h" + +#include <map> +#include <vector> + +#include "collada_internal.h" +#include "collada_utils.h" + +class ArmatureImporter : private TransformReader +{ +private: + Scene *scene; + UnitConverter *unit_converter; + + // std::map<int, JointData> joint_index_to_joint_info_map; + // std::map<COLLADAFW::UniqueId, int> joint_id_to_joint_index_map; + + struct LeafBone { + // COLLADAFW::Node *node; + EditBone *bone; + char name[32]; + float mat[4][4]; // bone matrix, derived from inv_bind_mat + }; + std::vector<LeafBone> leaf_bones; + // int bone_direction_row; // XXX not used + float leaf_bone_length; + int totbone; + // XXX not used + // float min_angle; // minimum angle between bone head-tail and a row of bone matrix + +#if 0 + struct ArmatureJoints { + Object *ob_arm; + std::vector<COLLADAFW::Node*> root_joints; + }; + std::vector<ArmatureJoints> armature_joints; +#endif + + Object *empty; // empty for leaf bones + + std::map<COLLADAFW::UniqueId, COLLADAFW::UniqueId> geom_uid_by_controller_uid; + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> joint_by_uid; // contains all joints + std::vector<COLLADAFW::Node*> root_joints; + std::map<COLLADAFW::UniqueId, Object*> joint_parent_map; + + MeshImporterBase *mesh_importer; + AnimationImporterBase *anim_importer; + + // This is used to store data passed in write_controller_data. + // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members + // so that arrays don't get freed until we free them explicitly. + + std::map<COLLADAFW::UniqueId, SkinInfo> skin_by_data_uid; // data UID = skin controller data UID +#if 0 + JointData *get_joint_data(COLLADAFW::Node *node); +#endif + + void create_bone(SkinInfo& skin, COLLADAFW::Node *node, EditBone *parent, int totchild, + float parent_mat[][4], bArmature *arm); + + void add_leaf_bone(float mat[][4], EditBone *bone); + + void fix_leaf_bones(); + +#if 0 + void set_leaf_bone_shapes(Object *ob_arm); + void set_euler_rotmode(); +#endif + + Object *get_empty_for_leaves(); + +#if 0 + Object *find_armature(COLLADAFW::Node *node); + + ArmatureJoints& get_armature_joints(Object *ob_arm); +#endif + + void create_armature_bones(SkinInfo& skin); + +public: + + ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce); + ~ArmatureImporter(); + + // root - if this joint is the top joint in hierarchy, if a joint + // is a child of a node (not joint), root should be true since + // this is where we build armature bones from + void add_joint(COLLADAFW::Node *node, bool root, Object *parent); + +#if 0 + void add_root_joint(COLLADAFW::Node *node); +#endif + + // here we add bones to armatures, having armatures previously created in write_controller + void make_armatures(bContext *C); + +#if 0 + // link with meshes, create vertex groups, assign weights + void link_armature(Object *ob_arm, const COLLADAFW::UniqueId& geom_id, const COLLADAFW::UniqueId& controller_data_id); +#endif + + bool write_skin_controller_data(const COLLADAFW::SkinControllerData* data); + + bool write_controller(const COLLADAFW::Controller* controller); + + COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId& controller_uid); + + Object *get_armature_for_joint(COLLADAFW::Node *node); + + void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count); + + // gives a world-space mat + bool get_joint_bind_mat(float m[][4], COLLADAFW::Node *joint); +}; + +#endif diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt index a72ecc4be6c..3f8abb7ed5d 100644 --- a/source/blender/collada/CMakeLists.txt +++ b/source/blender/collada/CMakeLists.txt @@ -24,45 +24,93 @@ # # ***** END GPL LICENSE BLOCK ***** -FILE(GLOB SRC *.cpp) +remove_strict_flags() -IF(APPLE) -SET(INC - . - ../blenlib - ../blenkernel - ../windowmanager - ../makesdna - ../makesrna - ../editors/include - ../../../intern/guardedalloc - ${OPENCOLLADA_INC}/COLLADAStreamWriter - ${OPENCOLLADA_INC}/COLLADABaseUtils - ${OPENCOLLADA_INC}/COLLADAFramework - ${OPENCOLLADA_INC}/COLLADASaxFrameworkLoader +set(INC + . + ../blenlib + ../blenkernel + ../blenloader + ../windowmanager + ../makesdna + ../makesrna + ../editors/include + ../../../intern/guardedalloc ) -ELSE(APPLE) +if(APPLE) + list(APPEND INC + ${OPENCOLLADA_INC}/COLLADAStreamWriter + ${OPENCOLLADA_INC}/COLLADABaseUtils + ${OPENCOLLADA_INC}/COLLADAFramework + ${OPENCOLLADA_INC}/COLLADASaxFrameworkLoader + ) +else() + list(APPEND INC + ${OPENCOLLADA_INC}/COLLADAStreamWriter/include + ${OPENCOLLADA_INC}/COLLADABaseUtils/include + ${OPENCOLLADA_INC}/COLLADAFramework/include + ${OPENCOLLADA_INC}/COLLADASaxFrameworkLoader/include + ) +endif() -SET(INC - . - ../blenlib - ../blenkernel - ../windowmanager - ../makesdna - ../makesrna - ../editors/include - ../../../intern/guardedalloc - ${OPENCOLLADA_INC}/COLLADAStreamWriter/include - ${OPENCOLLADA_INC}/COLLADABaseUtils/include - ${OPENCOLLADA_INC}/COLLADAFramework/include - ${OPENCOLLADA_INC}/COLLADASaxFrameworkLoader/include +set(SRC + AnimationImporter.cpp + ArmatureExporter.cpp + ArmatureImporter.cpp + CameraExporter.cpp + DocumentExporter.cpp + DocumentImporter.cpp + EffectExporter.cpp + GeometryExporter.cpp + ImageExporter.cpp + InstanceWriter.cpp + LightExporter.cpp + MaterialExporter.cpp + MeshImporter.cpp + SkinInfo.cpp + TransformReader.cpp + TransformWriter.cpp + collada.cpp + collada_internal.cpp + collada_utils.cpp + + AnimationImporter.h + ArmatureExporter.h + ArmatureImporter.h + CameraExporter.h + DocumentExporter.h + DocumentImporter.h + EffectExporter.h + GeometryExporter.h + ImageExporter.h + InstanceWriter.h + LightExporter.h + MaterialExporter.h + MeshImporter.h + SkinInfo.h + TransformReader.h + TransformWriter.h + collada.h + collada_internal.h + collada_utils.h ) -ENDIF(APPLE) +if(WITH_BUILDINFO) + add_definitions(-DNAN_BUILDINFO) +endif() +<<<<<<< .working IF(WITH_BUILDINFO) ADD_DEFINITIONS(-DNAN_BUILDINFO) ENDIF(WITH_BUILDINFO) BLENDERLIB(bf_collada "${SRC}" "${INC}") +======= +if(CMAKE_COMPILER_IS_GNUCXX) + # COLLADAFWArray.h gives error with gcc 4.5 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") +endif() + +blender_add_lib(bf_collada "${SRC}" "${INC}") +>>>>>>> .merge-right.r35190 diff --git a/source/blender/collada/CameraExporter.cpp b/source/blender/collada/CameraExporter.cpp new file mode 100644 index 00000000000..b2c9dd9a7c8 --- /dev/null +++ b/source/blender/collada/CameraExporter.cpp @@ -0,0 +1,86 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string> + +#include "COLLADASWCamera.h" +#include "COLLADASWCameraOptic.h" + +#include "DNA_camera_types.h" + +#include "CameraExporter.h" + +#include "collada_internal.h" + +CamerasExporter::CamerasExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryCameras(sw){} + +template<class Functor> +void forEachCameraObjectInScene(Scene *sce, Functor &f) +{ + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + if (ob->type == OB_CAMERA && ob->data) { + f(ob, sce); + } + base= base->next; + } +} + +void CamerasExporter::exportCameras(Scene *sce) +{ + openLibrary(); + + forEachCameraObjectInScene(sce, *this); + + closeLibrary(); +} +void CamerasExporter::operator()(Object *ob, Scene *sce) +{ + // TODO: shiftx, shifty, YF_dofdist + Camera *cam = (Camera*)ob->data; + std::string cam_id(get_camera_id(ob)); + std::string cam_name(id_name(cam)); + + if (cam->type == CAM_PERSP) { + COLLADASW::PerspectiveOptic persp(mSW); + persp.setXFov(lens_to_angle(cam->lens)*(180.0f/M_PI)); + persp.setAspectRatio((float)(sce->r.xsch)/(float)(sce->r.ysch)); + persp.setZFar(cam->clipend); + persp.setZNear(cam->clipsta); + COLLADASW::Camera ccam(mSW, &persp, cam_id, cam_name); + addCamera(ccam); + } + else { + COLLADASW::OrthographicOptic ortho(mSW); + ortho.setXMag(cam->ortho_scale); + ortho.setAspectRatio((float)(sce->r.xsch)/(float)(sce->r.ysch)); + ortho.setZFar(cam->clipend); + ortho.setZNear(cam->clipsta); + COLLADASW::Camera ccam(mSW, &ortho, cam_id, cam_name); + addCamera(ccam); + } +} diff --git a/source/blender/collada/CameraExporter.h b/source/blender/collada/CameraExporter.h new file mode 100644 index 00000000000..922eaf6b1d0 --- /dev/null +++ b/source/blender/collada/CameraExporter.h @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file CameraExporter.h + * \ingroup collada + */ + +#ifndef __CAMERAEXPORTER_H__ +#define __CAMERAEXPORTER_H__ + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryCameras.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +class CamerasExporter: COLLADASW::LibraryCameras +{ +public: + CamerasExporter(COLLADASW::StreamWriter *sw); + void exportCameras(Scene *sce); + void operator()(Object *ob, Scene *sce); +}; + +#endif diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp index 5c059a4e9c9..1a648f43097 100644 --- a/source/blender/collada/DocumentExporter.cpp +++ b/source/blender/collada/DocumentExporter.cpp @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -25,6 +25,8 @@ #include <stdio.h> #include <math.h> +extern "C" +{ #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_meshdata_types.h" @@ -32,16 +34,13 @@ #include "DNA_image_types.h" #include "DNA_material_types.h" #include "DNA_texture_types.h" -#include "DNA_camera_types.h" -#include "DNA_lamp_types.h" #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_curve_types.h" #include "DNA_armature_types.h" #include "DNA_modifier_types.h" +#include "DNA_userdef_types.h" -extern "C" -{ #include "BKE_DerivedMesh.h" #include "BKE_fcurve.h" #include "BKE_animsys.h" @@ -70,10 +69,11 @@ extern char build_rev[]; #include "BLI_string.h" #include "BLI_listbase.h" +#include "RNA_access.h" + #include "COLLADASWAsset.h" #include "COLLADASWLibraryVisualScenes.h" #include "COLLADASWNode.h" -#include "COLLADASWLibraryGeometries.h" #include "COLLADASWSource.h" #include "COLLADASWInstanceGeometry.h" #include "COLLADASWInputList.h" @@ -90,16 +90,12 @@ extern char build_rev[]; #include "COLLADASWSurfaceInitOption.h" #include "COLLADASWSampler.h" #include "COLLADASWScene.h" -//#include "COLLADASWSurface.h" #include "COLLADASWTechnique.h" #include "COLLADASWTexture.h" #include "COLLADASWLibraryMaterials.h" #include "COLLADASWBindMaterial.h" -#include "COLLADASWLibraryCameras.h" -#include "COLLADASWLibraryLights.h" #include "COLLADASWInstanceCamera.h" #include "COLLADASWInstanceLight.h" -#include "COLLADASWCameraOptic.h" #include "COLLADASWConstants.h" #include "COLLADASWLibraryControllers.h" #include "COLLADASWInstanceController.h" @@ -108,35 +104,22 @@ extern char build_rev[]; #include "collada_internal.h" #include "DocumentExporter.h" +// can probably go after refactor is complete +#include "InstanceWriter.h" +#include "TransformWriter.h" + +#include "ArmatureExporter.h" +#include "CameraExporter.h" +#include "EffectExporter.h" +#include "GeometryExporter.h" +#include "ImageExporter.h" +#include "LightExporter.h" +#include "MaterialExporter.h" + #include <vector> #include <algorithm> // std::find -// arithb.c now has QuatToAxisAngle too -#if 0 -// This function assumes that quat is normalized. -// The following document was used as reference: -// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm -void quat_to_axis_angle( float *axis, float *angle,float *q) -{ - // quat to axis angle - *angle = 2 * acos(q[0]); - float divisor = sqrt(1 - q[0] * q[0]); - - // test to avoid divide by zero, divisor is always positive - if (divisor < 0.001f ) { - axis[0] = 1.0f; - axis[1] = 0.0f; - axis[2] = 0.0f; - } - else { - axis[0] = q[1] / divisor; - axis[1] = q[2] / divisor; - axis[2] = q[3] / divisor; - } -} -#endif - -char *CustomData_get_layer_name(const struct CustomData *data, int type, int n) +char *bc_CustomData_get_layer_name(const struct CustomData *data, int type, int n) { int layer_index = CustomData_get_layer_index(data, type); if(layer_index < 0) return NULL; @@ -144,7 +127,7 @@ char *CustomData_get_layer_name(const struct CustomData *data, int type, int n) return data->layers[layer_index+n].name; } -char *CustomData_get_active_layer_name(const CustomData *data, int type) +char *bc_CustomData_get_active_layer_name(const CustomData *data, int type) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -153,108 +136,12 @@ char *CustomData_get_active_layer_name(const CustomData *data, int type) return data->layers[layer_index].name; } -/** -Translation map. -Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be -included. Look at the IDREF XSD declaration for more. -Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, -like special chars (e.g. micro sign), umlauts and so on. -The COLLADA spec also allows additional chars for member access ('.'), these -must obviously be removed too, otherwise they would be heavily misinterpreted. -*/ -const unsigned char translate_map[256] = { - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 45, 95, 95, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 95, 95, 95, 95, 95, 95, - 95, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 95, 95, 95, 95, 95, - 95, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 183, - 95, 95, 95, 95, 95, 95, 95, 95, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 95, - 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 95, - 248, 249, 250, 251, 252, 253, 254, 255}; - -/** Look at documentation of translate_map */ -static std::string translate_id(const std::string &id) -{ - std::string id_translated = id; - for (unsigned int i=0; i < id_translated.size(); i++) - { - id_translated[i] = translate_map[(unsigned int)id_translated[i]]; - } - return id_translated; -} - -static std::string id_name(void *id) -{ - return ((ID*)id)->name + 2; -} - -static std::string get_geometry_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-mesh"; -} - -static std::string get_light_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-light"; -} - -static std::string get_camera_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-camera"; -} - -std::string get_joint_id(Bone *bone, Object *ob_arm) -{ - return translate_id(id_name(ob_arm) + "_" + bone->name); -} - /* Utilities to avoid code duplication. Definition can take some time to understand, but they should be useful. */ -// f should have -// void operator()(Object* ob) -template<class Functor> -void forEachMeshObjectInScene(Scene *sce, Functor &f) -{ - - Base *base= (Base*) sce->base.first; - while(base) { - Object *ob = base->object; - - if (ob->type == OB_MESH && ob->data) { - f(ob); - } - base= base->next; - - } -} template<class Functor> void forEachObjectInScene(Scene *sce, Functor &f) @@ -269,1078 +156,7 @@ void forEachObjectInScene(Scene *sce, Functor &f) } } -template<class Functor> -void forEachCameraObjectInScene(Scene *sce, Functor &f) -{ - Base *base= (Base*) sce->base.first; - while(base) { - Object *ob = base->object; - - if (ob->type == OB_CAMERA && ob->data) { - f(ob, sce); - } - base= base->next; - } -} - -template<class Functor> -void forEachLampObjectInScene(Scene *sce, Functor &f) -{ - Base *base= (Base*) sce->base.first; - while(base) { - Object *ob = base->object; - - if (ob->type == OB_LAMP && ob->data) { - f(ob); - } - base= base->next; - } -} - -// used in forEachMaterialInScene -template <class MaterialFunctor> -class ForEachMaterialFunctor -{ - std::vector<std::string> mMat; // contains list of material names, to avoid duplicate calling of f - MaterialFunctor *f; -public: - ForEachMaterialFunctor(MaterialFunctor *f) : f(f) { } - void operator ()(Object *ob) - { - int a; - for(a = 0; a < ob->totcol; a++) { - - Material *ma = give_current_material(ob, a+1); - - if (!ma) continue; - - std::string translated_id = translate_id(id_name(ma)); - if (find(mMat.begin(), mMat.end(), translated_id) == mMat.end()) { - (*this->f)(ma, ob); - - mMat.push_back(translated_id); - } - } - } -}; - -// calls f for each unique material linked to each object in sce -// f should have -// void operator()(Material* ma) -template<class Functor> -void forEachMaterialInScene(Scene *sce, Functor &f) -{ - ForEachMaterialFunctor<Functor> matfunc(&f); - forEachMeshObjectInScene(sce, matfunc); -} - -// OB_MESH is assumed -std::string getActiveUVLayerName(Object *ob) -{ - Mesh *me = (Mesh*)ob->data; - - int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); - if (num_layers) - return std::string(CustomData_get_active_layer_name(&me->fdata, CD_MTFACE)); - - return ""; -} - -// TODO: optimize UV sets by making indexed list with duplicates removed -class GeometryExporter : COLLADASW::LibraryGeometries -{ - struct Face - { - unsigned int v1, v2, v3, v4; - }; - - struct Normal - { - float x, y, z; - }; - - Scene *mScene; - -public: - GeometryExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryGeometries(sw) {} - - void exportGeom(Scene *sce) - { - openLibrary(); - - mScene = sce; - forEachMeshObjectInScene(sce, *this); - - closeLibrary(); - } - - 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_id = get_geometry_id(ob); - std::vector<Normal> nor; - std::vector<Face> norind; - - bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); - - create_normals(nor, norind, me); - - // openMesh(geoId, geoName, meshId) - openMesh(geom_id); - - // writes <source> for vertex coords - createVertsSource(geom_id, me); - - // writes <source> for normal coords - createNormalsSource(geom_id, me, nor); - - bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE); - - // writes <source> for uv coords if mesh has uv coords - if (has_uvs) - createTexcoordsSource(geom_id, me); - - if (has_color) - createVertexColorSource(geom_id, me); - - // <vertices> - COLLADASW::Vertices verts(mSW); - verts.setId(getIdBySemantics(geom_id, COLLADASW::VERTEX)); - COLLADASW::InputList &input_list = verts.getInputList(); - COLLADASW::Input input(COLLADASW::POSITION, getUrlBySemantics(geom_id, COLLADASW::POSITION)); - input_list.push_back(input); - verts.add(); - - // XXX slow - if (ob->totcol) { - 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, has_color, ob, geom_id, norind); - } - } - else { - createPolylist(false, 0, has_uvs, has_color, ob, geom_id, norind); - } - - closeMesh(); - closeGeometry(); - -#if 0 - dm->release(dm); -#endif - } - - // powerful because it handles both cases when there is material and when there's not - void createPolylist(bool has_material, - int material_index, - bool has_uvs, - bool has_color, - Object *ob, - std::string& geom_id, - std::vector<Face>& norind) - { - Mesh *me = (Mesh*)ob->data; - MFace *mfaces = me->mface; - int totfaces = me->totface; - - // <vcount> - int i; - int faces_in_polylist = 0; - std::vector<unsigned long> vcount_list; - - // count faces with this material - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - - if ((has_material && f->mat_nr == material_index) || !has_material) { - faces_in_polylist++; - if (f->v4 == 0) { - vcount_list.push_back(3); - } - else { - vcount_list.push_back(4); - } - } - } - - // no faces using this material - if (faces_in_polylist == 0) { - return; - } - - Material *ma = has_material ? give_current_material(ob, material_index + 1) : NULL; - COLLADASW::Polylist polylist(mSW); - - // sets count attribute in <polylist> - polylist.setCount(faces_in_polylist); - - // sets material name - if (has_material) { - polylist.setMaterial(translate_id(id_name(ma))); - } - - COLLADASW::InputList &til = polylist.getInputList(); - - // creates <input> in <polylist> for vertices - COLLADASW::Input input1(COLLADASW::VERTEX, getUrlBySemantics(geom_id, COLLADASW::VERTEX), 0); - - // creates <input> in <polylist> for normals - COLLADASW::Input input2(COLLADASW::NORMAL, getUrlBySemantics(geom_id, COLLADASW::NORMAL), 1); - - til.push_back(input1); - til.push_back(input2); - - // if mesh has uv coords writes <input> for TEXCOORD - int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); - - 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_id, i)), - 2, // offset always 2, this is only until we have optimized UV sets - i // set number equals UV layer index - ); - til.push_back(input3); - } - - if (has_color) { - COLLADASW::Input input4(COLLADASW::COLOR, getUrlBySemantics(geom_id, COLLADASW::COLOR), has_uvs ? 3 : 2); - til.push_back(input4); - } - - // sets <vcount> - polylist.setVCountList(vcount_list); - - // performs the actual writing - polylist.prepareToAppendValues(); - - // <p> - int texindex = 0; - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - - if ((has_material && f->mat_nr == material_index) || !has_material) { - - unsigned int *v = &f->v1; - unsigned int *n = &norind[i].v1; - for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { - polylist.appendValues(v[j]); - polylist.appendValues(n[j]); - - if (has_uvs) - polylist.appendValues(texindex + j); - - if (has_color) - polylist.appendValues(texindex + j); - } - } - - texindex += 3; - if (f->v4 != 0) - texindex++; - } - - polylist.finish(); - } - - // creates <source> for positions - 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_id, COLLADASW::POSITION)); - source.setArrayId(getIdBySemantics(geom_id, COLLADASW::POSITION) + - ARRAY_ID_SUFFIX); - source.setAccessorCount(totverts); - source.setAccessorStride(3); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - /*main function, it creates <source id = "">, <float_array id = "" - count = ""> */ - source.prepareToAppendValues(); - //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.finish(); - - } - - void createVertexColorSource(std::string geom_id, Mesh *me) - { - if (!CustomData_has_layer(&me->fdata, CD_MCOL)) - return; - - MFace *f; - int totcolor = 0, i, j; - - for (i = 0, f = me->mface; i < me->totface; i++, f++) - totcolor += f->v4 ? 4 : 3; - - COLLADASW::FloatSourceF source(mSW); - source.setId(getIdBySemantics(geom_id, COLLADASW::COLOR)); - source.setArrayId(getIdBySemantics(geom_id, COLLADASW::COLOR) + ARRAY_ID_SUFFIX); - source.setAccessorCount(totcolor); - source.setAccessorStride(3); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("R"); - param.push_back("G"); - param.push_back("B"); - - source.prepareToAppendValues(); - - int index = CustomData_get_active_layer_index(&me->fdata, CD_MCOL); - - MCol *mcol = (MCol*)me->fdata.layers[index].data; - MCol *c = mcol; - - for (i = 0, f = me->mface; i < me->totface; i++, c += 4, f++) - for (j = 0; j < (f->v4 ? 4 : 3); j++) - source.appendValues(c[j].b / 255.0f, c[j].g / 255.0f, c[j].r / 255.0f); - - source.finish(); - } - - std::string makeTexcoordSourceId(std::string& geom_id, int layer_index) - { - char suffix[20]; - sprintf(suffix, "-%d", layer_index); - return getIdBySemantics(geom_id, COLLADASW::TEXCOORD) + suffix; - } - - //creates <source> for texcoords - 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; - - // count totuv - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - if (f->v4 == 0) { - totuv+=3; - } - else { - totuv+=4; - } - } - - int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); - - // write <source> for each layer - // each <source> will get id like meshName + "map-channel-1" - for (int a = 0; a < num_layers; a++) { - MTFace *tface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a); - // char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a); - - COLLADASW::FloatSourceF source(mSW); - std::string layer_id = makeTexcoordSourceId(geom_id, a); - source.setId(layer_id); - source.setArrayId(layer_id + ARRAY_ID_SUFFIX); - - source.setAccessorCount(totuv); - source.setAccessorStride(2); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("S"); - param.push_back("T"); - - source.prepareToAppendValues(); - - for (i = 0; i < totfaces; i++) { - MFace *f = &mfaces[i]; - - for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { - source.appendValues(tface[i].uv[j][0], - tface[i].uv[j][1]); - } - } - - source.finish(); - } - } - - - //creates <source> for normals - void createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal>& nor) - { -#if 0 - int totverts = dm->getNumVerts(dm); - MVert *verts = dm->getVertArray(dm); -#endif - - COLLADASW::FloatSourceF source(mSW); - source.setId(getIdBySemantics(geom_id, COLLADASW::NORMAL)); - source.setArrayId(getIdBySemantics(geom_id, COLLADASW::NORMAL) + - ARRAY_ID_SUFFIX); - source.setAccessorCount(nor.size()); - source.setAccessorStride(3); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - - source.prepareToAppendValues(); - - std::vector<Normal>::iterator it; - for (it = nor.begin(); it != nor.end(); it++) { - Normal& n = *it; - source.appendValues(n.x, n.y, n.z); - } - - source.finish(); - } - - void create_normals(std::vector<Normal> &nor, std::vector<Face> &ind, Mesh *me) - { - int i, j, v; - MVert *vert = me->mvert; - std::map<unsigned int, unsigned int> nshar; - - for (i = 0; i < me->totface; i++) { - MFace *fa = &me->mface[i]; - Face f; - unsigned int *nn = &f.v1; - unsigned int *vv = &fa->v1; - - memset(&f, 0, sizeof(f)); - v = fa->v4 == 0 ? 3 : 4; - - if (!(fa->flag & ME_SMOOTH)) { - Normal n; - if (v == 4) - normal_quad_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co, vert[fa->v4].co); - else - normal_tri_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co); - nor.push_back(n); - } - - for (j = 0; j < v; j++) { - if (fa->flag & ME_SMOOTH) { - if (nshar.find(*vv) != nshar.end()) - *nn = nshar[*vv]; - else { - Normal n = { - vert[*vv].no[0]/32767.0, - vert[*vv].no[1]/32767.0, - vert[*vv].no[2]/32767.0 - }; - nor.push_back(n); - *nn = nor.size() - 1; - nshar[*vv] = *nn; - } - vv++; - } - else { - *nn = nor.size() - 1; - } - nn++; - } - - ind.push_back(f); - } - } - - 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_id, COLLADASW::Semantics type, std::string other_suffix = "") { - - std::string id(getIdBySemantics(geom_id, type, other_suffix)); - return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); - - } - - COLLADASW::URI makeUrl(std::string id) - { - return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); - } - - - /* int getTriCount(MFace *faces, int totface) { - int i; - int tris = 0; - for (i = 0; i < totface; i++) { - // if quad - if (faces[i].v4 != 0) - tris += 2; - else - tris++; - } - - return tris; - }*/ -}; - -class TransformWriter : protected TransformBase -{ -protected: - void add_node_transform(COLLADASW::Node& node, float mat[][4], float parent_mat[][4]) - { - float loc[3], rot[3], scale[3]; - float local[4][4]; - - if (parent_mat) { - float invpar[4][4]; - invert_m4_m4(invpar, parent_mat); - mul_m4_m4m4(local, mat, invpar); - } - else { - copy_m4_m4(local, mat); - } - - TransformBase::decompose(local, loc, rot, NULL, scale); - - add_transform(node, loc, rot, scale); - } - - void add_node_transform_ob(COLLADASW::Node& node, Object *ob) - { - float rot[3], loc[3], scale[3]; - - if (ob->parent) { - float C[4][4], tmat[4][4], imat[4][4], mat[4][4]; - - // factor out scale from obmat - - copy_v3_v3(scale, ob->size); - - ob->size[0] = ob->size[1] = ob->size[2] = 1.0f; - object_to_mat4(ob, C); - copy_v3_v3(ob->size, scale); - mul_serie_m4(tmat, ob->parent->obmat, ob->parentinv, C, NULL, NULL, NULL, NULL, NULL); - - // calculate local mat - - invert_m4_m4(imat, ob->parent->obmat); - mul_m4_m4m4(mat, tmat, imat); - - // done - - mat4_to_eul(rot, mat); - copy_v3_v3(loc, mat[3]); - } - else { - copy_v3_v3(loc, ob->loc); - copy_v3_v3(rot, ob->rot); - copy_v3_v3(scale, ob->size); - } - - add_transform(node, loc, rot, scale); - } - - void add_node_transform_identity(COLLADASW::Node& node) - { - float loc[] = {0.0f, 0.0f, 0.0f}, scale[] = {1.0f, 1.0f, 1.0f}, rot[] = {0.0f, 0.0f, 0.0f}; - add_transform(node, loc, rot, scale); - } - -private: - void add_transform(COLLADASW::Node& node, float loc[3], float rot[3], float scale[3]) - { - 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", scale[0], scale[1], scale[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)); - matid = translate_id(matid); - 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) {} - - // 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, ob); - - 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); - - closeLibrary(); - } - - 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; - } - else { - ModifierData *mod = (ModifierData*)ob->modifiers.first; - while (mod) { - if (mod->type == eModifierType_Armature) { - ob_arm = ((ArmatureModifierData*)mod)->object; - } - - mod = mod->next; - } - } - - return ob_arm; - } - - std::string get_joint_sid(Bone *bone, Object *ob_arm) - { - return get_joint_id(bone, ob_arm); - } - - // 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, ob_arm); - - 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) - { - 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]; - invert_m4_m4(invpar, parchan->pose_mat); - mul_m4_m4m4(mat, pchan->pose_mat, invpar); - } - else { - // get world-space from armature-space - mul_m4_m4m4(mat, pchan->pose_mat, ob_arm->obmat); - } - - TransformWriter::add_node_transform(node, mat, NULL); - } - - std::string get_controller_id(Object *ob_arm, Object *ob) - { - return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + SKIN_CONTROLLER_ID_SUFFIX; - } - - // ob should be of type OB_MESH - // both args are required - void export_controller(Object* ob, Object *ob_arm) - { - // joint names - // joint inverse bind matrices - // vertex weights - - // input: - // joint names: ob -> vertex group names - // vertex group weights: me->dvert -> groups -> index, weight - - /* - me->dvert: - - typedef struct MDeformVert { - struct MDeformWeight *dw; - int totweight; - int flag; // flag only in use for weightpaint now - } MDeformVert; - - typedef struct MDeformWeight { - int def_nr; - float weight; - } MDeformWeight; - */ - - Mesh *me = (Mesh*)ob->data; - if (!me->dvert) return; - - std::string controller_name = id_name(ob_arm); - std::string controller_id = get_controller_id(ob_arm, ob); - - 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_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, ob_arm, &ob->defbase); - - closeSkin(); - closeController(); - } - - void add_joints_element(ListBase *defbase, - const std::string& joints_source_id, const std::string& inv_bind_mat_source_id) - { - COLLADASW::JointsElement joints(mSW); - COLLADASW::InputList &input = joints.getInputList(); - - input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id))); - input.push_back(COLLADASW::Input(COLLADASW::BINDMATRIX, - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id))); - joints.add(); - } - - void add_bind_shape_mat(Object *ob) - { - double bind_mat[4][4]; - - converter.mat4_to_dae_double(bind_mat, ob->obmat); - - addBindShapeTransform(bind_mat); - } - - 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(totjoint); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("JOINT"); - - source.prepareToAppendValues(); - - for (def = (bDeformGroup*)defbase->first; def; def = def->next) { - Bone *bone = get_bone_from_defgroup(ob_arm, def); - if (bone) - source.appendValues(get_joint_sid(bone, ob_arm)); - } - - source.finish(); - - return source_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; - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(BLI_countlist(defbase)); - source.setAccessorStride(16); - - source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("TRANSFORM"); - - source.prepareToAppendValues(); - - bPose *pose = ob_arm->pose; - bArmature *arm = (bArmature*)ob_arm->data; - - int flag = arm->flag; - - // put armature in rest position - if (!(arm->flag & ARM_RESTPOS)) { - arm->flag |= ARM_RESTPOS; - where_is_pose(scene, ob_arm); - } - - for (bDeformGroup *def = (bDeformGroup*)defbase->first; def; def = def->next) { - if (is_bone_defgroup(ob_arm, def)) { - - 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 - mul_m4_m4m4(world, pchan->pose_mat, ob_arm->obmat); - - invert_m4_m4(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(); - - 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(totweight); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("WEIGHT"); - - source.prepareToAppendValues(); - - // NOTE: COLLADA spec says weights should be normalized - - 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); - } - } - - source.finish(); - - return source_id; - } - - 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(); - - int offset = 0; - input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), offset++)); - input.push_back(COLLADASW::Input(COLLADASW::WEIGHT, - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), offset++)); - - weights.setCount(me->totvert); - - // write number of deformers per vertex - COLLADASW::PrimitivesBase::VCountList vcount; - int i; - for (i = 0; i < me->totvert; i++) { - vcount.push_back(me->dvert[i].totweight); - } - - weights.prepareToAppendVCountValues(); - weights.appendVertexCount(vcount); - - // 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++) { - weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]); - weights.appendValues(weight_index++); - } - } - - weights.finish(); - } -}; class SceneExporter: COLLADASW::LibraryVisualScenes, protected TransformWriter, protected InstanceWriter { @@ -1474,6 +290,7 @@ public: } }; +<<<<<<< .working class ImagesExporter: COLLADASW::LibraryImages { const char *mfilename; @@ -1911,16 +728,19 @@ public: } }; +======= +>>>>>>> .merge-right.r35190 // TODO: it would be better to instantiate animations rather than create a new one per object // COLLADA allows this through multiple <channel>s in <animation>. // For this to work, we need to know objects that use a certain action. class AnimationExporter: COLLADASW::LibraryAnimations { Scene *scene; + COLLADASW::StreamWriter *sw; public: - AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) {} + AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) { this->sw = sw; } void exportAnimations(Scene *sce) { @@ -1978,22 +798,22 @@ protected: openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source - std::string input_id = create_source_from_fcurve(Sampler::INPUT, fcu, anim_id, axis_name); + std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name); // create output source - std::string output_id = create_source_from_fcurve(Sampler::OUTPUT, fcu, anim_id, axis_name); + std::string 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->totvert, anim_id, axis_name); std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; - COLLADASW::LibraryAnimations::Sampler sampler(sampler_id); + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); std::string empty; - sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id)); - sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id)); + 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(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); addSampler(sampler); @@ -2154,28 +974,28 @@ protected: openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING); // create input source - std::string input_id = create_source_from_vector(Sampler::INPUT, fra, is_rot, anim_id, axis_name); + 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(v, fra.size(), anim_id); else - output_id = create_source_from_array(Sampler::OUTPUT, v, fra.size(), is_rot, anim_id, axis_name); + output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, v, fra.size(), is_rot, anim_id, axis_name); // create interpolations source std::string interpolation_id = create_interpolation_source(fra.size(), anim_id, axis_name); std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX; - COLLADASW::LibraryAnimations::Sampler sampler(sampler_id); + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); std::string empty; - sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id)); - sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id)); + 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(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id)); addSampler(sampler); @@ -2195,18 +1015,18 @@ protected: return COLLADABU::Math::Utils::radToDegF(angle); } - std::string get_semantic_suffix(Sampler::Semantic semantic) + std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) { switch(semantic) { - case Sampler::INPUT: + case COLLADASW::InputSemantic::INPUT: return INPUT_SOURCE_ID_SUFFIX; - case Sampler::OUTPUT: + case COLLADASW::InputSemantic::OUTPUT: return OUTPUT_SOURCE_ID_SUFFIX; - case Sampler::INTERPOLATION: + case COLLADASW::InputSemantic::INTERPOLATION: return INTERPOLATION_SOURCE_ID_SUFFIX; - case Sampler::IN_TANGENT: + case COLLADASW::InputSemantic::IN_TANGENT: return INTANGENT_SOURCE_ID_SUFFIX; - case Sampler::OUT_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: return OUTTANGENT_SOURCE_ID_SUFFIX; default: break; @@ -2215,13 +1035,13 @@ protected: } void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param, - Sampler::Semantic semantic, bool is_rot, const char *axis) + COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis) { switch(semantic) { - case Sampler::INPUT: + case COLLADASW::InputSemantic::INPUT: param.push_back("TIME"); break; - case Sampler::OUTPUT: + case COLLADASW::InputSemantic::OUTPUT: if (is_rot) { param.push_back("ANGLE"); } @@ -2236,8 +1056,8 @@ protected: } } break; - case Sampler::IN_TANGENT: - case Sampler::OUT_TANGENT: + case COLLADASW::InputSemantic::IN_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: param.push_back("X"); param.push_back("Y"); break; @@ -2246,14 +1066,14 @@ protected: } } - void get_source_values(BezTriple *bezt, Sampler::Semantic semantic, bool rotation, float *values, int *length) + void get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool rotation, float *values, int *length) { switch (semantic) { - case Sampler::INPUT: + case COLLADASW::InputSemantic::INPUT: *length = 1; values[0] = convert_time(bezt->vec[1][0]); break; - case Sampler::OUTPUT: + case COLLADASW::InputSemantic::OUTPUT: *length = 1; if (rotation) { values[0] = convert_angle(bezt->vec[1][1]); @@ -2262,8 +1082,8 @@ protected: values[0] = bezt->vec[1][1]; } break; - case Sampler::IN_TANGENT: - case Sampler::OUT_TANGENT: + case COLLADASW::InputSemantic::IN_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: // XXX *length = 2; break; @@ -2273,7 +1093,7 @@ protected: } } - std::string create_source_from_fcurve(Sampler::Semantic semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name) + std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name) { std::string source_id = anim_id + get_semantic_suffix(semantic); @@ -2307,7 +1127,7 @@ protected: return source_id; } - std::string create_source_from_array(Sampler::Semantic semantic, float *v, int tot, bool is_rot, const std::string& anim_id, const char *axis_name) + std::string 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); @@ -2324,7 +1144,7 @@ protected: for (int i = 0; i < tot; i++) { float val = v[i]; - if (semantic == Sampler::INPUT) + if (semantic == COLLADASW::InputSemantic::INPUT) val = convert_time(val); else if (is_rot) val = convert_angle(val); @@ -2336,7 +1156,7 @@ protected: return source_id; } - std::string create_source_from_vector(Sampler::Semantic semantic, std::vector<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name) + std::string 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); @@ -2354,7 +1174,7 @@ protected: std::vector<float>::iterator it; for (it = fra.begin(); it != fra.end(); it++) { float val = *it; - if (semantic == Sampler::INPUT) + if (semantic == COLLADASW::InputSemantic::INPUT) val = convert_time(val); else if (is_rot) val = convert_angle(val); @@ -2369,7 +1189,7 @@ protected: // only used for sources with OUTPUT semantic std::string create_xyz_source(float *v, int tot, const std::string& anim_id) { - Sampler::Semantic semantic = Sampler::OUTPUT; + COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; std::string source_id = anim_id + get_semantic_suffix(semantic); COLLADASW::FloatSourceF source(mSW); @@ -2395,7 +1215,7 @@ protected: std::string create_interpolation_source(int tot, const std::string& anim_id, const char *axis_name) { - std::string source_id = anim_id + get_semantic_suffix(Sampler::INTERPOLATION); + std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); COLLADASW::NameSource source(mSW); source.setId(source_id); @@ -2494,8 +1314,8 @@ protected: 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) - ; + /*else if (rotmode == ROT_MODE_AXISANGLE) + ;*/ } // enable fcurves driving a specific bone, disable all the rest @@ -2524,18 +1344,68 @@ protected: void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename) { + PointerRNA sceneptr, unit_settings; + PropertyRNA *system; /* unused , *scale; */ + + clear_global_id_map(); + COLLADABU::NativeString native_filename = COLLADABU::NativeString(std::string(filename)); COLLADASW::StreamWriter sw(native_filename); - // open <Collada> + // open <collada> sw.startDocument(); // <asset> COLLADASW::Asset asset(&sw); - // XXX ask blender devs about this? - asset.setUnit("decimetre", 0.1); + + RNA_id_pointer_create(&(sce->id), &sceneptr); + unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); + system = RNA_struct_find_property(&unit_settings, "system"); + //scale = RNA_struct_find_property(&unit_settings, "scale_length"); + + std::string unitname = "meter"; + float linearmeasure = 1.0f; + + linearmeasure = RNA_float_get(&unit_settings, "scale_length"); + + switch(RNA_property_enum_get(&unit_settings, system)) { + case USER_UNIT_NONE: + case USER_UNIT_METRIC: + if(linearmeasure == 0.001f) { + unitname = "millimeter"; + } + else if(linearmeasure == 0.01f) { + unitname = "centimeter"; + } + else if(linearmeasure == 0.1f) { + unitname = "decimeter"; + } + else if(linearmeasure == 1.0f) { + unitname = "meter"; + } + else if(linearmeasure == 1000.0f) { + unitname = "kilometer"; + } + break; + case USER_UNIT_IMPERIAL: + if(linearmeasure == 0.0254f) { + unitname = "inch"; + } + else if(linearmeasure == 0.3048f) { + unitname = "foot"; + } + else if(linearmeasure == 0.9144f) { + unitname = "yard"; + } + break; + default: + break; + } + + asset.setUnit(unitname, linearmeasure); asset.setUpAxisType(COLLADASW::Asset::Z_UP); +<<<<<<< .working // TODO: need an Author field in userpref asset.getContributor().mAuthor = "Blender User"; #ifdef NAN_BUILDINFO @@ -2545,6 +1415,22 @@ void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename) #else asset.getContributor().mAuthoringTool = "Blender 2.5x"; #endif +======= + // TODO: need an Author field in userpref + if(strlen(U.author) > 0) { + asset.getContributor().mAuthor = U.author; + } + else { + asset.getContributor().mAuthor = "Blender User"; + } +#ifdef NAN_BUILDINFO + char version_buf[128]; + sprintf(version_buf, "Blender %d.%02d.%d r%s", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION, build_rev); + asset.getContributor().mAuthoringTool = version_buf; +#else + asset.getContributor().mAuthoringTool = "Blender 2.5x"; +#endif +>>>>>>> .merge-right.r35190 asset.add(); // <library_cameras> diff --git a/source/blender/collada/DocumentExporter.h b/source/blender/collada/DocumentExporter.h index bb6d400fdf1..9d6d2114cd8 100644 --- a/source/blender/collada/DocumentExporter.h +++ b/source/blender/collada/DocumentExporter.h @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -21,6 +21,14 @@ * * ***** END GPL LICENSE BLOCK ***** */ + +/** \file DocumentExporter.h + * \ingroup collada + */ + +#ifndef __DOCUMENTEXPORTER_H__ +#define __DOCUMENTEXPORTER_H__ + struct Scene; class DocumentExporter @@ -29,3 +37,5 @@ class DocumentExporter void exportCurrentScene(Scene *sce, const char* filename); void exportScenes(const char* filename); }; + +#endif diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index b9052d23511..2267cf747b1 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -25,3204 +25,68 @@ // * name imported objects // * import object rotation as euler +#include <string> +#include <map> +#include <algorithm> // sort() + #include "COLLADAFWRoot.h" -#include "COLLADAFWIWriter.h" #include "COLLADAFWStableHeaders.h" -#include "COLLADAFWAnimationCurve.h" -#include "COLLADAFWAnimationList.h" -#include "COLLADAFWCamera.h" #include "COLLADAFWColorOrTexture.h" -#include "COLLADAFWEffect.h" -#include "COLLADAFWFloatOrDoubleArray.h" -#include "COLLADAFWGeometry.h" -#include "COLLADAFWImage.h" #include "COLLADAFWIndexList.h" -#include "COLLADAFWInstanceGeometry.h" -#include "COLLADAFWLight.h" -#include "COLLADAFWMaterial.h" -#include "COLLADAFWMesh.h" #include "COLLADAFWMeshPrimitiveWithFaceVertexCount.h" -#include "COLLADAFWNode.h" #include "COLLADAFWPolygons.h" #include "COLLADAFWSampler.h" -#include "COLLADAFWSkinController.h" -#include "COLLADAFWSkinControllerData.h" -#include "COLLADAFWTransformation.h" -#include "COLLADAFWTranslate.h" -#include "COLLADAFWRotate.h" -#include "COLLADAFWScale.h" -#include "COLLADAFWMatrix.h" #include "COLLADAFWTypes.h" #include "COLLADAFWVisualScene.h" -#include "COLLADAFWFileInfo.h" #include "COLLADAFWArrayPrimitiveType.h" +#include "COLLADAFWLibraryNodes.h" +#include "COLLADAFWCamera.h" +#include "COLLADAFWLight.h" #include "COLLADASaxFWLLoader.h" -// TODO move "extern C" into header files -extern "C" -{ -#include "ED_keyframing.h" -#include "ED_armature.h" -#include "ED_mesh.h" // ED_vgroup_vert_add, ... -#include "ED_anim_api.h" -#include "ED_object.h" - -#include "WM_types.h" -#include "WM_api.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_main.h" -#include "BKE_customdata.h" #include "BKE_library.h" #include "BKE_texture.h" #include "BKE_fcurve.h" #include "BKE_depsgraph.h" #include "BLI_path_util.h" -#include "BKE_displist.h" -#include "BLI_math.h" #include "BKE_scene.h" -} -#include "BKE_armature.h" -#include "BKE_mesh.h" #include "BKE_global.h" -#include "BKE_context.h" -#include "BKE_object.h" -#include "BKE_image.h" #include "BKE_material.h" #include "BKE_utildefines.h" -#include "BKE_action.h" - -#include "BLI_math.h" -#include "BLI_listbase.h" -#include "BLI_string.h" +#include "BKE_image.h" -#include "DNA_lamp_types.h" -#include "DNA_armature_types.h" -#include "DNA_anim_types.h" -#include "DNA_curve_types.h" -#include "DNA_texture_types.h" #include "DNA_camera_types.h" -#include "DNA_object_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_mesh_types.h" -#include "DNA_material_types.h" -#include "DNA_scene_types.h" -#include "DNA_modifier_types.h" +#include "DNA_lamp_types.h" + +#include "RNA_access.h" #include "MEM_guardedalloc.h" #include "DocumentImporter.h" #include "collada_internal.h" -#include <string> -#include <map> -#include <algorithm> // sort() - -#include <math.h> -#include <float.h> - -// #define COLLADA_DEBUG - -// creates empties for each imported bone on layer 2, for debugging -// #define ARMATURE_TEST - -char *CustomData_get_layer_name(const struct CustomData *data, int type, int n); - -static const char *primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type) -{ - using namespace COLLADAFW; - - switch (type) { - case MeshPrimitive::LINES: - return "LINES"; - case MeshPrimitive::LINE_STRIPS: - return "LINESTRIPS"; - case MeshPrimitive::POLYGONS: - return "POLYGONS"; - case MeshPrimitive::POLYLIST: - return "POLYLIST"; - case MeshPrimitive::TRIANGLES: - return "TRIANGLES"; - case MeshPrimitive::TRIANGLE_FANS: - return "TRIANGLE_FANS"; - case MeshPrimitive::TRIANGLE_STRIPS: - return "TRIANGLE_FANS"; - case MeshPrimitive::POINTS: - return "POINTS"; - case MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE: - return "UNDEFINED_PRIMITIVE_TYPE"; - } - return "UNKNOWN"; -} - -static const char *geomTypeToStr(COLLADAFW::Geometry::GeometryType type) -{ - switch (type) { - case COLLADAFW::Geometry::GEO_TYPE_MESH: - return "MESH"; - case COLLADAFW::Geometry::GEO_TYPE_SPLINE: - return "SPLINE"; - case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH: - return "CONVEX_MESH"; - case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN: - default: - return "UNKNOWN"; - } -} - -// works for COLLADAFW::Node, COLLADAFW::Geometry -template<class T> -static const char *get_dae_name(T *node) -{ - const std::string& name = node->getName(); - return name.size() ? name.c_str() : node->getOriginalId().c_str(); -} - -// use this for retrieving bone names, since these must be unique -template<class T> -static const char *get_joint_name(T *node) -{ - const std::string& id = node->getOriginalId(); - return id.size() ? id.c_str() : node->getName().c_str(); -} - -static float get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index) -{ - if (index >= array.getValuesCount()) - return 0.0f; - - if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) - return array.getFloatValues()->getData()[index]; - else - return array.getDoubleValues()->getData()[index]; -} - -// copied from /editors/object/object_relations.c -static int test_parent_loop(Object *par, Object *ob) -{ - /* test if 'ob' is a parent somewhere in par's parents */ - - if(par == NULL) return 0; - if(ob == par) return 1; - - return test_parent_loop(par->parent, ob); -} - -// a shortened version of parent_set_exec() -// if is_parent_space is true then ob->obmat will be multiplied by par->obmat before parenting -static int set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space=true) -{ - if (!par || test_parent_loop(par, ob)) - return false; - - Object workob; - Main *bmain = CTX_data_main(C); - Scene *sce = CTX_data_scene(C); - - ob->parent = par; - ob->partype = PAROBJECT; - - ob->parsubstr[0] = 0; - - if (is_parent_space) { - // calc par->obmat - where_is_object(sce, par); - - // move child obmat into world space - float mat[4][4]; - mul_m4_m4m4(mat, ob->obmat, par->obmat); - copy_m4_m4(ob->obmat, mat); - } - - // apply child obmat (i.e. decompose it into rot/loc/size) - object_apply_mat4(ob, ob->obmat); - - // compute parentinv - what_does_parent(sce, ob, &workob); - invert_m4_m4(ob->parentinv, workob.obmat); - - ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA; - par->recalc |= OB_RECALC_OB; - - DAG_scene_sort(bmain, sce); - DAG_ids_flush_update(bmain, 0); - WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); - - return true; -} - -typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex*> > TexIndexTextureArrayMap; - -class TransformReader : public 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]; - - unit_m4(mat); - - for (unsigned 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: - dae_translate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::ROTATE: - dae_rotate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::SCALE: - dae_scale_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::MATRIX: - dae_matrix_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::LOOKAT: - case COLLADAFW::Transformation::SKEW: - fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n"); - break; - } - - copy_m4_m4(copy, mat); - mul_m4_m4m4(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; - } - } - } - - void dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) - { - COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm; - COLLADABU::Math::Vector3& axis = ro->getRotationAxis(); - float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f); - float ax[] = {axis[0], axis[1], axis[2]}; - // float quat[4]; - // axis_angle_to_quat(quat, axis, angle); - // quat_to_mat4(m, quat); - axis_angle_to_mat4(m, ax, angle); - } - - void dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) - { - COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm; - COLLADABU::Math::Vector3& t = tra->getTranslation(); - - unit_m4(m); - - m[3][0] = (float)t[0]; - m[3][1] = (float)t[1]; - m[3][2] = (float)t[2]; - } - - void dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) - { - COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale(); - float size[3] = {(float)s[0], (float)s[1], (float)s[2]}; - size_to_mat4(m, size); - } - - void dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) - { - unit_converter->dae_matrix_to_mat4(m, ((COLLADAFW::Matrix*)tm)->getMatrix()); - } - - void dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]) - { - dae_vector3_to_v3(((COLLADAFW::Translate*)tm)->getTranslation(), v); - } - - void dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]) - { - dae_vector3_to_v3(((COLLADAFW::Scale*)tm)->getScale(), v); - } - - void dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]) - { - v[0] = v3.x; - v[1] = v3.y; - v[2] = v3.z; - } -}; - -// only for ArmatureImporter to "see" MeshImporter::get_object_by_geom_uid -class MeshImporterBase -{ -public: - virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) = 0; -}; - -// ditto as above -class AnimationImporterBase -{ -public: - // virtual void change_eul_to_quat(Object *ob, bAction *act) = 0; -}; - -class ArmatureImporter : private TransformReader -{ -private: - Scene *scene; - UnitConverter *unit_converter; - - // std::map<int, JointData> joint_index_to_joint_info_map; - // std::map<COLLADAFW::UniqueId, int> joint_id_to_joint_index_map; - - struct LeafBone { - // COLLADAFW::Node *node; - EditBone *bone; - char name[32]; - float mat[4][4]; // bone matrix, derived from inv_bind_mat - }; - std::vector<LeafBone> leaf_bones; - // int bone_direction_row; // XXX not used - float leaf_bone_length; - int totbone; - // XXX not used - // float min_angle; // minimum angle between bone head-tail and a row of bone matrix - -#if 0 - struct ArmatureJoints { - Object *ob_arm; - std::vector<COLLADAFW::Node*> root_joints; - }; - std::vector<ArmatureJoints> armature_joints; -#endif - - Object *empty; // empty for leaf bones - - std::map<COLLADAFW::UniqueId, COLLADAFW::UniqueId> geom_uid_by_controller_uid; - std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> joint_by_uid; // contains all joints - std::vector<COLLADAFW::Node*> root_joints; - std::map<COLLADAFW::UniqueId, Object*> joint_parent_map; - - MeshImporterBase *mesh_importer; - AnimationImporterBase *anim_importer; - - // This is used to store data passed in write_controller_data. - // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members - // so that arrays don't get freed until we free them explicitly. - class SkinInfo - { - private: - // to build armature bones from inverse bind matrices - struct JointData { - float inv_bind_mat[4][4]; // joint inverse bind matrix - COLLADAFW::UniqueId joint_uid; // joint node UID - // Object *ob_arm; // armature object - }; - - float bind_shape_matrix[4][4]; - - // data from COLLADAFW::SkinControllerData, each array should be freed - COLLADAFW::UIntValuesArray joints_per_vertex; - COLLADAFW::UIntValuesArray weight_indices; - COLLADAFW::IntValuesArray joint_indices; - // COLLADAFW::FloatOrDoubleArray weights; - std::vector<float> weights; - - std::vector<JointData> joint_data; // index to this vector is joint index - - UnitConverter *unit_converter; - - Object *ob_arm; - COLLADAFW::UniqueId controller_uid; - Object *parent; - - public: - - SkinInfo() {} - - SkinInfo(const SkinInfo& skin) : weights(skin.weights), - joint_data(skin.joint_data), - unit_converter(skin.unit_converter), - ob_arm(skin.ob_arm), - controller_uid(skin.controller_uid), - parent(skin.parent) - { - copy_m4_m4(bind_shape_matrix, (float (*)[4])skin.bind_shape_matrix); - - 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), parent(NULL) {} - - // nobody owns the data after this, so it should be freed manually with releaseMemory - template <class T> - void transfer_array_data(T& src, T& dest) - { - 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_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(); - } - - void borrow_skin_controller_data(const COLLADAFW::SkinControllerData* skin) - { - transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getJointsPerVertex(), joints_per_vertex); - transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getWeightIndices(), weight_indices); - transfer_array_data((COLLADAFW::IntValuesArray&)skin->getJointIndices(), joint_indices); - // transfer_array_data(skin->getWeights(), weights); - - // cannot transfer data for FloatOrDoubleArray, copy values manually - const COLLADAFW::FloatOrDoubleArray& weight = skin->getWeights(); - for (unsigned int i = 0; i < weight.getValuesCount(); i++) - weights.push_back(get_float_value(weight, i)); - - unit_converter->dae_matrix_to_mat4(bind_shape_matrix, skin->getBindShapeMatrix()); - } - - void free() - { - joints_per_vertex.releaseMemory(); - weight_indices.releaseMemory(); - joint_indices.releaseMemory(); - // weights.releaseMemory(); - } - - // using inverse bind matrices to construct armature - // it is safe to invert them to get the original matrices - // because if they are inverse matrices, they can be inverted - void add_joint(const COLLADABU::Math::Matrix4& matrix) - { - JointData jd; - unit_converter->dae_matrix_to_mat4(jd.inv_bind_mat, matrix); - joint_data.push_back(jd); - } - - void set_controller(const COLLADAFW::SkinController* co) - { - controller_uid = co->getUniqueId(); - - // fill in joint UIDs - const COLLADAFW::UniqueIdArray& joint_uids = co->getJoints(); - for (unsigned int i = 0; i < joint_uids.getCount(); i++) { - joint_data[i].joint_uid = joint_uids[i]; - - // // store armature pointer - // JointData& jd = joint_index_to_joint_info_map[i]; - // jd.ob_arm = ob_arm; - - // now we'll be able to get inv bind matrix from joint id - // joint_id_to_joint_index_map[joint_ids[i]] = i; - } - } - - // called from write_controller - Object *create_armature(Scene *scene) - { - ob_arm = add_object(scene, OB_ARMATURE); - return ob_arm; - } - - Object* set_armature(Object *ob_arm) - { - if (this->ob_arm) - return this->ob_arm; - - this->ob_arm = ob_arm; - return ob_arm; - } - - bool get_joint_inv_bind_matrix(float inv_bind_mat[][4], COLLADAFW::Node *node) - { - const COLLADAFW::UniqueId& uid = node->getUniqueId(); - std::vector<JointData>::iterator it; - for (it = joint_data.begin(); it != joint_data.end(); it++) { - if ((*it).joint_uid == uid) { - copy_m4_m4(inv_bind_mat, (*it).inv_bind_mat); - return true; - } - } - - return false; - } - - Object *get_armature() - { - return ob_arm; - } - - const COLLADAFW::UniqueId& get_controller_uid() - { - return controller_uid; - } - - // check if this skin controller references a joint or any descendant of it - // - // some nodes may not be referenced by SkinController, - // in this case to determine if the node belongs to this armature, - // we need to search down the tree - bool uses_joint_or_descendant(COLLADAFW::Node *node) - { - const COLLADAFW::UniqueId& uid = node->getUniqueId(); - std::vector<JointData>::iterator it; - for (it = joint_data.begin(); it != joint_data.end(); it++) { - if ((*it).joint_uid == uid) - return true; - } - - COLLADAFW::NodePointerArray& children = node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (uses_joint_or_descendant(children[i])) - return true; - } - - return false; - } - - void link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, - TransformReader *tm) - { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature); - ((ArmatureModifierData *)md)->object = ob_arm; - - copy_m4_m4(ob->obmat, bind_shape_matrix); - object_apply_mat4(ob, ob->obmat); -#if 1 - ::set_parent(ob, ob_arm, C); -#else - Object workob; - ob->parent = ob_arm; - ob->partype = PAROBJECT; - - what_does_parent(scene, ob, &workob); - invert_m4_m4(ob->parentinv, workob.obmat); - - ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA; - - DAG_scene_sort(bmain, scene); - DAG_ids_flush_update(bmain, 0); - WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); -#endif - - ((bArmature*)ob_arm->data)->deformflag = ARM_DEF_VGROUP; - - // create all vertex groups - std::vector<JointData>::iterator it; - int joint_index; - for (it = joint_data.begin(), joint_index = 0; it != joint_data.end(); it++, joint_index++) { - const char *name = "Group"; - - // name group by joint node name - if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) { - name = get_joint_name(joint_by_uid[(*it).joint_uid]); - } - - ED_vgroup_add_name(ob, (char*)name); - } - - // <vcount> - number of joints per vertex - joints_per_vertex - // <v> - [[bone index, weight index] * joints per vertex] * vertices - weight indices - // ^ bone index can be -1 meaning weight toward bind shape, how to express this in Blender? - - // for each vertex in weight indices - // for each bone index in vertex - // add vertex to group at group index - // treat group index -1 specially - - // get def group by index with BLI_findlink - - for (unsigned int vertex = 0, weight = 0; vertex < joints_per_vertex.getCount(); vertex++) { - - unsigned int limit = weight + joints_per_vertex[vertex]; - for ( ; weight < limit; weight++) { - int joint = joint_indices[weight], joint_weight = weight_indices[weight]; - - // -1 means "weight towards the bind shape", we just don't assign it to any group - if (joint != -1) { - bDeformGroup *def = (bDeformGroup*)BLI_findlink(&ob->defbase, joint); - - ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE); - } - } - } - } - - bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node) - { - return get_pose_channel(ob_arm->pose, get_joint_name(node)); - } - - void set_parent(Object *_parent) - { - parent = _parent; - } - - Object* get_parent() - { - return parent; - } - - void find_root_joints(const std::vector<COLLADAFW::Node*> &root_joints, - std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, - std::vector<COLLADAFW::Node*>& result) - { - std::vector<COLLADAFW::Node*>::const_iterator it; - for (it = root_joints.begin(); it != root_joints.end(); it++) { - COLLADAFW::Node *root = *it; - std::vector<JointData>::iterator ji; - for (ji = joint_data.begin(); ji != joint_data.end(); ji++) { - COLLADAFW::Node *joint = joint_by_uid[(*ji).joint_uid]; - if (find_node_in_tree(joint, root)) { - if (std::find(result.begin(), result.end(), root) == result.end()) - result.push_back(root); - } - } - } - } - - bool find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root) - { - if (node == tree_root) - return true; - - COLLADAFW::NodePointerArray& children = tree_root->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (find_node_in_tree(node, children[i])) - return true; - } - - return false; - } - - }; - - std::map<COLLADAFW::UniqueId, SkinInfo> skin_by_data_uid; // data UID = skin controller data UID -#if 0 - JointData *get_joint_data(COLLADAFW::Node *node) - { - const COLLADAFW::UniqueId& joint_id = node->getUniqueId(); - - if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) { - fprintf(stderr, "Cannot find a joint index by joint id for %s.\n", - node->getOriginalId().c_str()); - return NULL; - } - - int joint_index = joint_id_to_joint_index_map[joint_id]; - - return &joint_index_to_joint_info_map[joint_index]; - } -#endif - - void create_bone(SkinInfo& skin, COLLADAFW::Node *node, EditBone *parent, int totchild, - float parent_mat[][4], bArmature *arm) - { - float joint_inv_bind_mat[4][4]; - - // JointData* jd = get_joint_data(node); - - float mat[4][4]; - - if (skin.get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { - // get original world-space matrix - invert_m4_m4(mat, joint_inv_bind_mat); - } - // create a bone even if there's no joint data for it (i.e. it has no influence) - else { - float obmat[4][4]; - - // object-space - get_node_mat(obmat, node, NULL, NULL); - - // get world-space - if (parent) - mul_m4_m4m4(mat, obmat, parent_mat); - else - copy_m4_m4(mat, obmat); - } - - // TODO rename from Node "name" attrs later - EditBone *bone = ED_armature_edit_bone_add(arm, (char*)get_joint_name(node)); - totbone++; - - if (parent) bone->parent = parent; - - // set head - copy_v3_v3(bone->head, mat[3]); - - // set tail, don't set it to head because 0-length bones are not allowed - float vec[3] = {0.0f, 0.5f, 0.0f}; - add_v3_v3v3(bone->tail, bone->head, vec); - - // set parent tail - if (parent && totchild == 1) { - copy_v3_v3(parent->tail, bone->head); - - // not setting BONE_CONNECTED because this would lock child bone location with respect to parent - // bone->flag |= BONE_CONNECTED; - - // XXX increase this to prevent "very" small bones? - const float epsilon = 0.000001f; - - // derive leaf bone length - float length = len_v3v3(parent->head, parent->tail); - if ((length < leaf_bone_length || totbone == 0) && length > epsilon) { - leaf_bone_length = length; - } - - // treat zero-sized bone like a leaf bone - if (length <= epsilon) { - add_leaf_bone(parent_mat, parent); - } - - /* -#if 0 - // and which row in mat is bone direction - float vec[3]; - sub_v3_v3v3(vec, parent->tail, parent->head); -#ifdef COLLADA_DEBUG - print_v3("tail - head", vec); - print_m4("matrix", parent_mat); -#endif - for (int i = 0; i < 3; i++) { -#ifdef COLLADA_DEBUG - char *axis_names[] = {"X", "Y", "Z"}; - printf("%s-axis length is %f\n", axis_names[i], len_v3(parent_mat[i])); -#endif - float angle = angle_v2v2(vec, parent_mat[i]); - if (angle < min_angle) { -#ifdef COLLADA_DEBUG - print_v3("picking", parent_mat[i]); - printf("^ %s axis of %s's matrix\n", axis_names[i], get_dae_name(node)); -#endif - bone_direction_row = i; - min_angle = angle; - } - } -#endif - */ - } - - COLLADAFW::NodePointerArray& children = node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - create_bone(skin, children[i], bone, children.getCount(), mat, arm); - } - - // in second case it's not a leaf bone, but we handle it the same way - if (!children.getCount() || children.getCount() > 1) { - add_leaf_bone(mat, bone); - } - } - - void add_leaf_bone(float mat[][4], EditBone *bone) - { - LeafBone leaf; - - leaf.bone = bone; - copy_m4_m4(leaf.mat, mat); - BLI_strncpy(leaf.name, bone->name, sizeof(leaf.name)); - - leaf_bones.push_back(leaf); - } - - void fix_leaf_bones() - { - // just setting tail for leaf bones here - - std::vector<LeafBone>::iterator it; - for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { - LeafBone& leaf = *it; - - // pointing up - float vec[3] = {0.0f, 0.0f, 1.0f}; - - mul_v3_fl(vec, leaf_bone_length); - - copy_v3_v3(leaf.bone->tail, leaf.bone->head); - add_v3_v3v3(leaf.bone->tail, leaf.bone->head, vec); - } - } - - void set_leaf_bone_shapes(Object *ob_arm) - { - bPose *pose = ob_arm->pose; - - std::vector<LeafBone>::iterator it; - for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { - LeafBone& leaf = *it; - - bPoseChannel *pchan = get_pose_channel(pose, leaf.name); - if (pchan) { - pchan->custom = get_empty_for_leaves(); - } - else { - fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name); - } - } - } - -#if 0 - void set_euler_rotmode() - { - // just set rotmode = ROT_MODE_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_or_descendant(joint)) { - bPoseChannel *pchan = skin.get_pose_channel_from_node(joint); - - if (pchan) { - pchan->rotmode = ROT_MODE_EUL; - } - else { - fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint)); - } - - break; - } - } - } - } -#endif - - Object *get_empty_for_leaves() - { - if (empty) return empty; - - empty = add_object(scene, OB_EMPTY); - empty->empty_drawtype = OB_EMPTY_SPHERE; - - return empty; - } - -#if 0 - Object *find_armature(COLLADAFW::Node *node) - { - JointData* jd = get_joint_data(node); - if (jd) return jd->ob_arm; - - COLLADAFW::NodePointerArray& children = node->getChildNodes(); - for (int i = 0; i < children.getCount(); i++) { - Object *ob_arm = find_armature(children[i]); - if (ob_arm) return ob_arm; - } - - return NULL; - } - - ArmatureJoints& get_armature_joints(Object *ob_arm) - { - // try finding it - std::vector<ArmatureJoints>::iterator it; - for (it = armature_joints.begin(); it != armature_joints.end(); it++) { - if ((*it).ob_arm == ob_arm) return *it; - } - - // not found, create one - ArmatureJoints aj; - aj.ob_arm = ob_arm; - armature_joints.push_back(aj); - - return armature_joints.back(); - } -#endif - - void create_armature_bones(SkinInfo& skin) - { - // just do like so: - // - get armature - // - enter editmode - // - add edit bones and head/tail properties using matrices and parent-child info - // - exit edit mode - // - set a sphere shape to leaf bones - - Object *ob_arm = NULL; - - /* - * find if there's another skin sharing at least one bone with this skin - * if so, use that skin's armature - */ - - /* - Pseudocode: - - find_node_in_tree(node, root_joint) - - skin::find_root_joints(root_joints): - std::vector root_joints; - for each root in root_joints: - for each joint in joints: - if find_node_in_tree(joint, root): - if (std::find(root_joints.begin(), root_joints.end(), root) == root_joints.end()) - root_joints.push_back(root); - - for (each skin B with armature) { - find all root joints for skin B - - for each joint X in skin A: - for each root joint R in skin B: - if (find_node_in_tree(X, R)) { - shared = 1; - goto endloop; - } - } - - endloop: - */ - - SkinInfo *a = &skin; - Object *shared = NULL; - std::vector<COLLADAFW::Node*> skin_root_joints; - - std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - SkinInfo *b = &it->second; - if (b == a || b->get_armature() == NULL) - continue; - - skin_root_joints.clear(); - - b->find_root_joints(root_joints, joint_by_uid, skin_root_joints); - - std::vector<COLLADAFW::Node*>::iterator ri; - for (ri = skin_root_joints.begin(); ri != skin_root_joints.end(); ri++) { - if (a->uses_joint_or_descendant(*ri)) { - shared = b->get_armature(); - break; - } - } - - if (shared != NULL) - break; - } - - if (shared) - ob_arm = skin.set_armature(shared); - else - ob_arm = skin.create_armature(scene); - - // enter armature edit mode - ED_armature_to_edit(ob_arm); - - leaf_bones.clear(); - totbone = 0; - // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on default row - leaf_bone_length = 0.1f; - // min_angle = 360.0f; // minimum angle between bone head-tail and a row of bone matrix - - // create bones - /* - TODO: - check if bones have already been created for a given joint - */ - - std::vector<COLLADAFW::Node*>::iterator ri; - for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { - // for shared armature check if bone tree is already created - if (shared && std::find(skin_root_joints.begin(), skin_root_joints.end(), *ri) != skin_root_joints.end()) - continue; - - // since root_joints may contain joints for multiple controllers, we need to filter - if (skin.uses_joint_or_descendant(*ri)) { - create_bone(skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, (bArmature*)ob_arm->data); - - if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) - skin.set_parent(joint_parent_map[(*ri)->getUniqueId()]); - } - } - - fix_leaf_bones(); - - // exit armature edit mode - ED_armature_from_edit(ob_arm); - ED_armature_edit_free(ob_arm); - DAG_id_flush_update(&ob_arm->id, OB_RECALC_OB|OB_RECALC_DATA); - - set_leaf_bone_shapes(ob_arm); - - // set_euler_rotmode(); - } - - -public: - - ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce) : - TransformReader(conv), scene(sce), empty(NULL), mesh_importer(mesh), anim_importer(anim) {} - - ~ArmatureImporter() - { - // free skin controller data if we forget to do this earlier - std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - it->second.free(); - } - } - - // root - if this joint is the top joint in hierarchy, if a joint - // is a child of a node (not joint), root should be true since - // this is where we build armature bones from - void add_joint(COLLADAFW::Node *node, bool root, Object *parent) - { - joint_by_uid[node->getUniqueId()] = node; - if (root) { - root_joints.push_back(node); - - if (parent) - joint_parent_map[node->getUniqueId()] = parent; - } - } - -#if 0 - void add_root_joint(COLLADAFW::Node *node) - { - // root_joints.push_back(node); - Object *ob_arm = find_armature(node); - if (ob_arm) { - get_armature_joints(ob_arm).root_joints.push_back(node); - } -#ifdef COLLADA_DEBUG - else { - fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node)); - } -#endif - } -#endif - - // here we add bones to armatures, having armatures previously created in write_controller - void make_armatures(bContext *C) - { - 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; - - create_armature_bones(skin); - - // link armature with a mesh object - Object *ob = mesh_importer->get_object_by_geom_uid(*get_geometry_uid(skin.get_controller_uid())); - if (ob) - skin.link_armature(C, ob, joint_by_uid, this); - else - fprintf(stderr, "Cannot find object to link armature with.\n"); - - // set armature parent if any - Object *par = skin.get_parent(); - if (par) - set_parent(skin.get_armature(), par, C, false); - - // free memory stolen from SkinControllerData - skin.free(); - } - } - -#if 0 - // link with meshes, create vertex groups, assign weights - void link_armature(Object *ob_arm, const COLLADAFW::UniqueId& geom_id, const COLLADAFW::UniqueId& controller_data_id) - { - Object *ob = mesh_importer->get_object_by_geom_uid(geom_id); - - if (!ob) { - fprintf(stderr, "Cannot find object by geometry UID.\n"); - return; - } - - if (skin_by_data_uid.find(controller_data_id) == skin_by_data_uid.end()) { - fprintf(stderr, "Cannot find skin info by controller data UID.\n"); - return; - } - - SkinInfo& skin = skin_by_data_uid[conroller_data_id]; - - // create vertex groups - } -#endif - - bool write_skin_controller_data(const COLLADAFW::SkinControllerData* data) - { - // at this stage we get vertex influence info that should go into me->verts and ob->defbase - // there's no info to which object this should be long so we associate it with skin controller data UID - - // don't forget to call defgroup_unique_name before we copy - - // controller data uid -> [armature] -> joint data, - // [mesh object] - // - - SkinInfo skin(unit_converter); - skin.borrow_skin_controller_data(data); - - // store join inv bind matrix to use it later in armature construction - const COLLADAFW::Matrix4Array& inv_bind_mats = data->getInverseBindMatrices(); - for (unsigned int i = 0; i < data->getJointsCount(); i++) { - skin.add_joint(inv_bind_mats[i]); - } - - skin_by_data_uid[data->getUniqueId()] = skin; - - return true; - } - - bool write_controller(const COLLADAFW::Controller* controller) - { - // - create and store armature object - - const COLLADAFW::UniqueId& skin_id = controller->getUniqueId(); - - if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) { - COLLADAFW::SkinController *co = (COLLADAFW::SkinController*)controller; - // to be able to find geom id by controller id - geom_uid_by_controller_uid[skin_id] = co->getSource(); - - const COLLADAFW::UniqueId& data_uid = co->getSkinControllerData(); - if (skin_by_data_uid.find(data_uid) == skin_by_data_uid.end()) { - fprintf(stderr, "Cannot find skin by controller data UID.\n"); - return true; - } - - skin_by_data_uid[data_uid].set_controller(co); - } - // morph controller - else { - // shape keys? :) - fprintf(stderr, "Morph controller is not supported yet.\n"); - } - - return true; - } - - COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId& controller_uid) - { - if (geom_uid_by_controller_uid.find(controller_uid) == geom_uid_by_controller_uid.end()) - return NULL; - - 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_or_descendant(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.bones[\"%s\"]", get_joint_name(node)); - } - - // gives a world-space mat - bool get_joint_bind_mat(float m[][4], COLLADAFW::Node *joint) - { - std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; - bool found = false; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - SkinInfo& skin = it->second; - if ((found = skin.get_joint_inv_bind_matrix(m, joint))) { - invert_m4(m); - break; - } - } - - return found; - } -}; - -class MeshImporter : public MeshImporterBase -{ -private: - - Scene *scene; - ArmatureImporter *armature_importer; - - std::map<COLLADAFW::UniqueId, Mesh*> uid_mesh_map; // geometry unique id-to-mesh map - std::map<COLLADAFW::UniqueId, Object*> uid_object_map; // geom uid-to-object - // this structure is used to assign material indices to faces - // it holds a portion of Mesh faces and corresponds to a DAE primitive list (<triangles>, <polylist>, etc.) - struct Primitive { - MFace *mface; - unsigned int totface; - }; - typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap; - std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name! - - class UVDataWrapper - { - COLLADAFW::MeshVertexData *mVData; - public: - UVDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata) - {} - -#ifdef COLLADA_DEBUG - void print() - { - fprintf(stderr, "UVs:\n"); - switch(mVData->getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: - { - COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues(); - if (values->getCount()) { - for (int i = 0; i < values->getCount(); i += 2) { - fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i+1]); - } - } - } - break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: - { - COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues(); - if (values->getCount()) { - for (int i = 0; i < values->getCount(); i += 2) { - fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i+1]); - } - } - } - break; - } - fprintf(stderr, "\n"); - } -#endif - - void getUV(int uv_index[2], float *uv) - { - switch(mVData->getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: - { - COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues(); - if (values->empty()) return; - uv[0] = (*values)[uv_index[0]]; - uv[1] = (*values)[uv_index[1]]; - - } - break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: - { - COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues(); - if (values->empty()) return; - uv[0] = (float)(*values)[uv_index[0]]; - uv[1] = (float)(*values)[uv_index[1]]; - - } - break; - case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: - default: - fprintf(stderr, "MeshImporter.getUV(): unknown data type\n"); - } - } - }; - - void set_face_indices(MFace *mface, unsigned int *indices, bool quad) - { - mface->v1 = indices[0]; - mface->v2 = indices[1]; - mface->v3 = indices[2]; - if (quad) mface->v4 = indices[3]; - else mface->v4 = 0; -#ifdef COLLADA_DEBUG - // fprintf(stderr, "%u, %u, %u \n", indices[0], indices[1], indices[2]); -#endif - } - - // not used anymore, test_index_face from blenkernel is better -#if 0 - // change face indices order so that v4 is not 0 - void rotate_face_indices(MFace *mface) { - mface->v4 = mface->v1; - mface->v1 = mface->v2; - mface->v2 = mface->v3; - mface->v3 = 0; - } -#endif - - void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, unsigned int *tris_indices) - { - int uv_indices[4][2]; - - // per face vertex indices, this means for quad we have 4 indices, not 8 - COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); - - // make indices into FloatOrDoubleArray - for (int i = 0; i < 3; i++) { - int uv_index = indices[tris_indices[i]]; - uv_indices[i][0] = uv_index * 2; - uv_indices[i][1] = uv_index * 2 + 1; - } - - uvs.getUV(uv_indices[0], mtface->uv[0]); - uvs.getUV(uv_indices[1], mtface->uv[1]); - uvs.getUV(uv_indices[2], mtface->uv[2]); - } - - void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, - COLLADAFW::IndexList& index_list, int index, bool quad) - { - int uv_indices[4][2]; - - // per face vertex indices, this means for quad we have 4 indices, not 8 - COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); - - // make indices into FloatOrDoubleArray - for (int i = 0; i < (quad ? 4 : 3); i++) { - int uv_index = indices[index + i]; - uv_indices[i][0] = uv_index * 2; - uv_indices[i][1] = uv_index * 2 + 1; - } - - uvs.getUV(uv_indices[0], mtface->uv[0]); - uvs.getUV(uv_indices[1], mtface->uv[1]); - uvs.getUV(uv_indices[2], mtface->uv[2]); - - if (quad) uvs.getUV(uv_indices[3], mtface->uv[3]); - -#ifdef COLLADA_DEBUG - /*if (quad) { - fprintf(stderr, "face uv:\n" - "((%d, %d), (%d, %d), (%d, %d), (%d, %d))\n" - "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", - - uv_indices[0][0], uv_indices[0][1], - uv_indices[1][0], uv_indices[1][1], - uv_indices[2][0], uv_indices[2][1], - uv_indices[3][0], uv_indices[3][1], - - mtface->uv[0][0], mtface->uv[0][1], - mtface->uv[1][0], mtface->uv[1][1], - mtface->uv[2][0], mtface->uv[2][1], - mtface->uv[3][0], mtface->uv[3][1]); - } - else { - fprintf(stderr, "face uv:\n" - "((%d, %d), (%d, %d), (%d, %d))\n" - "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", - - uv_indices[0][0], uv_indices[0][1], - uv_indices[1][0], uv_indices[1][1], - uv_indices[2][0], uv_indices[2][1], - - mtface->uv[0][0], mtface->uv[0][1], - mtface->uv[1][0], mtface->uv[1][1], - mtface->uv[2][0], mtface->uv[2][1]); - }*/ -#endif - } - -#ifdef COLLADA_DEBUG - void print_index_list(COLLADAFW::IndexList& index_list) - { - fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str()); - for (int i = 0; i < index_list.getIndicesCount(); i += 2) { - fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1)); - } - fprintf(stderr, "\n"); - } -#endif - - bool is_nice_mesh(COLLADAFW::Mesh *mesh) - { - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - - const char *name = get_dae_name(mesh); - - for (unsigned i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType(); - - const char *type_str = primTypeToStr(type); - - // OpenCollada passes POLYGONS type for <polylist> - if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { - - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; - COLLADAFW::Polygons::VertexCountArray& vca = mpvc->getGroupedVerticesVertexCountArray(); - - for(unsigned int j = 0; j < vca.getCount(); j++){ - int count = vca[j]; - if (count < 3) { - fprintf(stderr, "Primitive %s in %s has at least one face with vertex count < 3\n", - type_str, name); - return false; - } - } - - } - else if(type != COLLADAFW::MeshPrimitive::TRIANGLES) { - fprintf(stderr, "Primitive type %s is not supported.\n", type_str); - return false; - } - } - - if (mesh->getPositions().empty()) { - fprintf(stderr, "Mesh %s has no vertices.\n", name); - return false; - } - - return true; - } - - void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) - { - // vertices - me->totvert = mesh->getPositions().getFloatValues()->getCount() / 3; - me->mvert = (MVert*)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); - - COLLADAFW::MeshVertexData& pos = mesh->getPositions(); - MVert *mvert; - int i; - - for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) - get_vector(mvert->co, pos, i); - } - - int triangulate_poly(unsigned int *indices, int totvert, MVert *verts, std::vector<unsigned int>& tri) - { - ListBase dispbase; - DispList *dl; - float *vert; - int i = 0; - - dispbase.first = dispbase.last = NULL; - - dl = (DispList*)MEM_callocN(sizeof(DispList), "poly disp"); - dl->nr = totvert; - dl->type = DL_POLY; - dl->parts = 1; - dl->verts = vert = (float*)MEM_callocN(totvert * 3 * sizeof(float), "poly verts"); - dl->index = (int*)MEM_callocN(sizeof(int) * 3 * totvert, "dl index"); - - BLI_addtail(&dispbase, dl); - - for (i = 0; i < totvert; i++) { - copy_v3_v3(vert, verts[indices[i]].co); - vert += 3; - } - - filldisplist(&dispbase, &dispbase, 0); - - int tottri = 0; - dl= (DispList*)dispbase.first; - - if (dl->type == DL_INDEX3) { - tottri = dl->parts; - - int *index = dl->index; - for (i= 0; i < tottri; i++) { - int t[3]= {*index, *(index + 1), *(index + 2)}; - - std::sort(t, t + 3); - - tri.push_back(t[0]); - tri.push_back(t[1]); - tri.push_back(t[2]); - - index += 3; - } - } - - freedisplist(&dispbase); - - return tottri; - } - - int count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me) - { - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - unsigned int i; - int tottri = 0; - - for (i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - int type = mp->getPrimitiveType(); - size_t prim_totface = mp->getFaceCount(); - unsigned int *indices = mp->getPositionIndices().getData(); - - if (type == COLLADAFW::MeshPrimitive::POLYLIST || - type == COLLADAFW::MeshPrimitive::POLYGONS) { - - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; - COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); - - for (unsigned int j = 0; j < prim_totface; j++) { - int vcount = vcounta[j]; - - if (vcount > 4) { - std::vector<unsigned int> tri; - - // tottri += triangulate_poly(indices, vcount, me->mvert, tri) - 1; // XXX why - 1?! - tottri += triangulate_poly(indices, vcount, me->mvert, tri); - } - - indices += vcount; - } - } - } - return tottri; - } - - // TODO: import uv set names - void read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris) - { - unsigned int i; - - // allocate faces - me->totface = mesh->getFacesCount() + new_tris; - me->mface = (MFace*)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface); - - // allocate UV layers - unsigned int totuvset = mesh->getUVCoords().getInputInfosArray().getCount(); - - // for (i = 0; i < totuvset; i++) { - // if (mesh->getUVCoords().getLength(i) == 0) { - // totuvset = 0; - // break; - // } - // } - - for (i = 0; i < totuvset; i++) { - CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface); - //this->set_layername_map[i] = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i); - } - - // activate the first uv layer - if (totuvset) me->mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, 0); - - UVDataWrapper uvs(mesh->getUVCoords()); - -#ifdef COLLADA_DEBUG - // uvs.print(); -#endif - - MFace *mface = me->mface; - - MaterialIdPrimitiveArrayMap mat_prim_map; - - int face_index = 0; - - COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); - - bool has_normals = mesh->hasNormals(); - COLLADAFW::MeshVertexData& nor = mesh->getNormals(); - - for (i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - - // faces - size_t prim_totface = mp->getFaceCount(); - unsigned int *indices = mp->getPositionIndices().getData(); - unsigned int *nind = mp->getNormalIndices().getData(); - unsigned int j, k; - int type = mp->getPrimitiveType(); - int index = 0; - - // since we cannot set mface->mat_nr here, we store a portion of me->mface in Primitive - Primitive prim = {mface, 0}; - COLLADAFW::IndexListArray& index_list_array = mp->getUVCoordIndicesArray(); - -#ifdef COLLADA_DEBUG - /* - fprintf(stderr, "Primitive %d:\n", i); - for (int j = 0; j < totuvset; j++) { - print_index_list(*index_list_array[j]); - } - */ -#endif - - if (type == COLLADAFW::MeshPrimitive::TRIANGLES) { - for (j = 0; j < prim_totface; j++){ - - set_face_indices(mface, indices, false); - indices += 3; - -#if 0 - for (k = 0; k < totuvset; k++) { - if (!index_list_array.empty() && index_list_array[k]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, false); - } - } -#else - for (k = 0; k < index_list_array.getCount(); k++) { - int uvset_index = index_list_array[k]->getSetIndex(); - - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, uvset_index); - set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, false); - } -#endif - - test_index_face(mface, &me->fdata, face_index, 3); - - if (has_normals) { - if (!flat_face(nind, nor, 3)) - mface->flag |= ME_SMOOTH; - - nind += 3; - } - - index += 3; - mface++; - face_index++; - prim.totface++; - } - } - else if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; - COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); - - for (j = 0; j < prim_totface; j++) { - - // face - int vcount = vcounta[j]; - if (vcount == 3 || vcount == 4) { - - set_face_indices(mface, indices, vcount == 4); - - // set mtface for each uv set - // it is assumed that all primitives have equal number of UV sets - -#if 0 - for (k = 0; k < totuvset; k++) { - if (!index_list_array.empty() && index_list_array[k]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); - set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, mface->v4 != 0); - } - } -#else - for (k = 0; k < index_list_array.getCount(); k++) { - int uvset_index = index_list_array[k]->getSetIndex(); - - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, uvset_index); - set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, mface->v4 != 0); - } -#endif - - test_index_face(mface, &me->fdata, face_index, vcount); - - if (has_normals) { - if (!flat_face(nind, nor, vcount)) - mface->flag |= ME_SMOOTH; - - nind += vcount; - } - - mface++; - face_index++; - prim.totface++; - - } - else { - std::vector<unsigned int> tri; - - triangulate_poly(indices, vcount, me->mvert, tri); - - for (k = 0; k < tri.size() / 3; k++) { - int v = k * 3; - unsigned int uv_indices[3] = { - index + tri[v], - index + tri[v + 1], - index + tri[v + 2] - }; - unsigned int tri_indices[3] = { - indices[tri[v]], - indices[tri[v + 1]], - indices[tri[v + 2]] - }; - - set_face_indices(mface, tri_indices, false); - -#if 0 - for (unsigned int l = 0; l < totuvset; l++) { - if (!index_list_array.empty() && index_list_array[l]) { - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, l); - set_face_uv(&mtface[face_index], uvs, l, *index_list_array[l], uv_indices); - } - } -#else - for (unsigned int l = 0; l < index_list_array.getCount(); l++) { - int uvset_index = index_list_array[l]->getSetIndex(); - - // get mtface by face index and uv set index - MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, uvset_index); - set_face_uv(&mtface[face_index], uvs, *index_list_array[l], uv_indices); - } -#endif - - - test_index_face(mface, &me->fdata, face_index, 3); - - if (has_normals) { - unsigned int ntri[3] = {nind[tri[v]], nind[tri[v + 1]], nind[tri[v + 2]]}; - - if (!flat_face(ntri, nor, 3)) - mface->flag |= ME_SMOOTH; - } - - mface++; - face_index++; - prim.totface++; - } - - if (has_normals) - nind += vcount; - } - - index += vcount; - indices += vcount; - } - } - - mat_prim_map[mp->getMaterialId()].push_back(prim); - } - - geom_uid_mat_mapping_map[mesh->getUniqueId()] = mat_prim_map; - } - - void get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i) - { - i *= 3; - - switch(arr.getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: - { - COLLADAFW::ArrayPrimitiveType<float>* values = arr.getFloatValues(); - if (values->empty()) return; - - v[0] = (*values)[i++]; - v[1] = (*values)[i++]; - v[2] = (*values)[i]; - } - break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: - { - COLLADAFW::ArrayPrimitiveType<double>* values = arr.getDoubleValues(); - if (values->empty()) return; - - v[0] = (float)(*values)[i++]; - v[1] = (float)(*values)[i++]; - v[2] = (float)(*values)[i]; - } - break; - default: - break; - } - } - - bool flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count) - { - float a[3], b[3]; - - get_vector(a, nor, *nind); - normalize_v3(a); - - nind++; - - for (int i = 1; i < count; i++, nind++) { - get_vector(b, nor, *nind); - normalize_v3(b); - - float dp = dot_v3v3(a, b); - - if (dp < 0.99999f || dp > 1.00001f) - return false; - } - - return true; - } - -public: - - MeshImporter(ArmatureImporter *arm, Scene *sce) : scene(sce), armature_importer(arm) {} - - virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) - { - if (uid_object_map.find(geom_uid) != uid_object_map.end()) - return uid_object_map[geom_uid]; - return NULL; - } - - MTex *assign_textures_to_uvlayer(COLLADAFW::TextureCoordinateBinding &ctexture, - Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map, - MTex *color_texture) - { - COLLADAFW::TextureMapId texture_index = ctexture.getTextureMapId(); - char *uvname = CustomData_get_layer_name(&me->fdata, CD_MTFACE, ctexture.getSetIndex()); - - if (texindex_texarray_map.find(texture_index) == texindex_texarray_map.end()) { - - fprintf(stderr, "Cannot find texture array by texture index.\n"); - return color_texture; - } - - std::vector<MTex*> textures = texindex_texarray_map[texture_index]; - - std::vector<MTex*>::iterator it; - - for (it = textures.begin(); it != textures.end(); it++) { - - MTex *texture = *it; - - if (texture) { - strcpy(texture->uvname, uvname); - if (texture->mapto == MAP_COL) color_texture = texture; - } - } - return color_texture; - } - - MTFace *assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, - std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, - Object *ob, const COLLADAFW::UniqueId *geom_uid, - MTex **color_texture, char *layername, MTFace *texture_face, - std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map, int mat_index) - { - Mesh *me = (Mesh*)ob->data; - const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial(); - - // do we know this material? - if (uid_material_map.find(ma_uid) == uid_material_map.end()) { - - fprintf(stderr, "Cannot find material by UID.\n"); - return NULL; - } - - Material *ma = uid_material_map[ma_uid]; - assign_material(ob, ma, ob->totcol + 1); - - COLLADAFW::TextureCoordinateBindingArray& tex_array = - cmaterial.getTextureCoordinateBindingArray(); - TexIndexTextureArrayMap texindex_texarray_map = material_texture_mapping_map[ma]; - unsigned int i; - // loop through <bind_vertex_inputs> - for (i = 0; i < tex_array.getCount(); i++) { - - *color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map, - *color_texture); - } - - // set texture face - if (*color_texture && - strlen((*color_texture)->uvname) && - strcmp(layername, (*color_texture)->uvname) != 0) { - - texture_face = (MTFace*)CustomData_get_layer_named(&me->fdata, CD_MTFACE, - (*color_texture)->uvname); - strcpy(layername, (*color_texture)->uvname); - } - - MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; - COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId(); - - // assign material indices to mesh faces - if (mat_prim_map.find(mat_id) != mat_prim_map.end()) { - - std::vector<Primitive>& prims = mat_prim_map[mat_id]; - - std::vector<Primitive>::iterator it; - - for (it = prims.begin(); it != prims.end(); it++) { - Primitive& prim = *it; - i = 0; - while (i++ < prim.totface) { - prim.mface->mat_nr = mat_index; - prim.mface++; - // bind texture images to faces - if (texture_face && (*color_texture)) { - texture_face->mode = TF_TEX; - texture_face->tpage = (Image*)(*color_texture)->tex->ima; - texture_face++; - } - } - } - } - - return texture_face; - } - - - Object *create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom, - bool isController, - std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, - std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map) - { - const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId(); - - // check if node instanciates controller or geometry - if (isController) { - - geom_uid = armature_importer->get_geometry_uid(*geom_uid); - - if (!geom_uid) { - fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n"); - return NULL; - } - } - else { - - if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) { - // this could happen if a mesh was not created - // (e.g. if it contains unsupported geometry) - fprintf(stderr, "Couldn't find a mesh by UID.\n"); - return NULL; - } - } - if (!uid_mesh_map[*geom_uid]) return NULL; - - Object *ob = add_object(scene, OB_MESH); - - // store object pointer for ArmatureImporter - uid_object_map[*geom_uid] = ob; - - // name Object - const std::string& id = node->getOriginalId(); - if (id.length()) - rename_id(&ob->id, (char*)id.c_str()); - - // replace ob->data freeing the old one - Mesh *old_mesh = (Mesh*)ob->data; - - set_mesh(ob, uid_mesh_map[*geom_uid]); - - if (old_mesh->id.us == 0) free_libblock(&G.main->mesh, old_mesh); - - char layername[100]; - MTFace *texture_face = NULL; - MTex *color_texture = NULL; - - COLLADAFW::MaterialBindingArray& mat_array = - geom->getMaterialBindings(); - - // loop through geom's materials - for (unsigned int i = 0; i < mat_array.getCount(); i++) { - - texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, - &color_texture, layername, texture_face, - material_texture_mapping_map, i); - } - - return ob; - } - - // create a mesh storing a pointer in a map so it can be retrieved later by geometry UID - bool write_geometry(const COLLADAFW::Geometry* geom) - { - // TODO: import also uvs, normals - // XXX what to do with normal indices? - // XXX num_normals may be != num verts, then what to do? - - // check geometry->getType() first - if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { - // TODO: report warning - fprintf(stderr, "Mesh type %s is not supported\n", geomTypeToStr(geom->getType())); - return true; - } - - COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh*)geom; - - if (!is_nice_mesh(mesh)) { - fprintf(stderr, "Ignoring mesh %s\n", get_dae_name(mesh)); - return true; - } - - const std::string& str_geom_id = mesh->getOriginalId(); - Mesh *me = add_mesh((char*)str_geom_id.c_str()); - - // store the Mesh pointer to link it later with an Object - this->uid_mesh_map[mesh->getUniqueId()] = me; - - int new_tris = 0; - - read_vertices(mesh, me); - - new_tris = count_new_tris(mesh, me); - - read_faces(mesh, me, new_tris); - - make_edges(me, 0); - - mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); - - return true; - } - -}; - -class AnimationImporter : private TransformReader, public AnimationImporterBase -{ -private: - - ArmatureImporter *armature_importer; - Scene *scene; - - std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > curve_map; - std::map<COLLADAFW::UniqueId, TransformReader::Animation> uid_animated_map; - // std::map<bActionGroup*, std::vector<FCurve*> > fcurves_actionGroup_map; - std::map<COLLADAFW::UniqueId, const COLLADAFW::AnimationList*> animlist_map; - std::vector<FCurve*> unused_curves; - std::map<COLLADAFW::UniqueId, Object*> joint_objects; - - FCurve *create_fcurve(int array_index, const char *rna_path) - { - FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve"); - - fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED); - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); - fcu->array_index = array_index; - return fcu; - } - - void create_bezt(FCurve *fcu, float frame, float output) - { - BezTriple bez; - memset(&bez, 0, sizeof(BezTriple)); - bez.vec[1][0] = frame; - bez.vec[1][1] = output; - 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, 0); - calchandles_fcurve(fcu); - } - - // create one or several fcurves depending on the number of parameters being animated - void animation_to_fcurves(COLLADAFW::AnimationCurve *curve) - { - COLLADAFW::FloatOrDoubleArray& input = curve->getInputValues(); - COLLADAFW::FloatOrDoubleArray& output = curve->getOutputValues(); - // COLLADAFW::FloatOrDoubleArray& intan = curve->getInTangentValues(); - // COLLADAFW::FloatOrDoubleArray& outtan = curve->getOutTangentValues(); - float fps = (float)FPS; - size_t dim = curve->getOutDimension(); - unsigned int i; - - std::vector<FCurve*>& fcurves = curve_map[curve->getUniqueId()]; - - switch (dim) { - case 1: // X, Y, Z or angle - case 3: // XYZ - case 16: // matrix - { - for (i = 0; i < dim; i++ ) { - 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 (unsigned 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 * dim + 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, 0); - } - - calchandles_fcurve(fcu); - - fcurves.push_back(fcu); - } - } - break; - default: - fprintf(stderr, "Output dimension of %d is not yet supported (animation id = %s)\n", dim, curve->getOriginalId().c_str()); - } - - for (std::vector<FCurve*>::iterator it = fcurves.begin(); it != fcurves.end(); it++) - unused_curves.push_back(*it); - } - - void fcurve_deg_to_rad(FCurve *cu) - { - for (unsigned int i = 0; i < cu->totvert; i++) { - // TODO convert handles too - cu->bezt[i].vec[1][1] *= M_PI / 180.0f; - } - } - - void add_fcurves_to_object(Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index, Animation *animated) - { - bAction *act; - - if (!ob->adt || !ob->adt->action) act = verify_adt_action((ID*)&ob->id, 1); - else act = ob->adt->action; - - std::vector<FCurve*>::iterator it; - int i; - -#if 0 - char *p = strstr(rna_path, "rotation_euler"); - bool is_rotation = p && *(p + strlen("rotation_euler")) == '\0'; - - // convert degrees to radians for rotation - if (is_rotation) - fcurve_deg_to_rad(fcu); -#endif - - for (it = curves.begin(), i = 0; it != curves.end(); it++, i++) { - FCurve *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; - - if (ob->type == OB_ARMATURE) { - bActionGroup *grp = NULL; - const char *bone_name = get_joint_name(animated->node); - - if (bone_name) { - /* try to find group */ - grp = action_groups_find_named(act, bone_name); - - /* no matching groups, so add one */ - if (grp == NULL) { - /* Add a new group, and make it active */ - grp = (bActionGroup*)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); - - grp->flag = AGRP_SELECTED; - BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); - - BLI_addtail(&act->groups, grp); - BLI_uniquename(&act->groups, grp, "Group", '.', offsetof(bActionGroup, name), 64); - } - - /* add F-Curve to group */ - action_groups_add_channel(act, grp, fcu); - - } -#if 0 - if (is_rotation) { - fcurves_actionGroup_map[grp].push_back(fcu); - } -#endif - } - else { - BLI_addtail(&act->curves, fcu); - } - - // curve is used, so remove it from unused_curves - unused_curves.erase(std::remove(unused_curves.begin(), unused_curves.end(), fcu), unused_curves.end()); - } - } -public: - - AnimationImporter(UnitConverter *conv, ArmatureImporter *arm, Scene *scene) : - TransformReader(conv), armature_importer(arm), scene(scene) { } - - ~AnimationImporter() - { - // free unused FCurves - for (std::vector<FCurve*>::iterator it = unused_curves.begin(); it != unused_curves.end(); it++) - free_fcurve(*it); - - if (unused_curves.size()) - fprintf(stderr, "removed %u unused curves\n", unused_curves.size()); - } - - bool write_animation(const COLLADAFW::Animation* anim) - { - if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) { - COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim; - - // 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; - } - - // 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: - case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER: - animation_to_fcurves(curve); - break; - default: - // TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types - 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* animlist) - { - const COLLADAFW::UniqueId& animlist_id = animlist->getUniqueId(); - - animlist_map[animlist_id] = animlist; - -#if 0 - // should not happen - if (uid_animated_map.find(animlist_id) == uid_animated_map.end()) { - return true; - } - - // for bones rna_path is like: pose.bones["bone-name"].rotation - - // what does this AnimationList animate? - Animation& animated = uid_animated_map[animlist_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 = animlist->getAnimationBindings(); - - switch (animated.tm->getTransformationType()) { - case COLLADAFW::Transformation::TRANSLATE: - case COLLADAFW::Transformation::SCALE: - { - bool loc = animated.tm->getTransformationType() == COLLADAFW::Transformation::TRANSLATE; - if (is_joint) - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, loc ? "location" : "scale"); - else - BLI_strncpy(rna_path, loc ? "location" : "scale", 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 (curve_map.find(anim_uid) == curve_map.end()) { - fprintf(stderr, "Cannot find FCurve by animation UID.\n"); - continue; - } - - std::vector<FCurve*>& fcurves = curve_map[anim_uid]; - - switch (binding.animationClass) { - case COLLADAFW::AnimationList::POSITION_X: - add_fcurves_to_object(ob, fcurves, rna_path, 0, &animated); - break; - case COLLADAFW::AnimationList::POSITION_Y: - add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated); - break; - case COLLADAFW::AnimationList::POSITION_Z: - add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated); - break; - case COLLADAFW::AnimationList::POSITION_XYZ: - add_fcurves_to_object(ob, fcurves, rna_path, -1, &animated); - break; - default: - fprintf(stderr, "AnimationClass %d is not supported for %s.\n", - binding.animationClass, loc ? "TRANSLATE" : "SCALE"); - } - } - } - break; - case COLLADAFW::Transformation::ROTATE: - { - if (is_joint) - BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_euler", joint_path); - else - BLI_strncpy(rna_path, "rotation_euler", 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 (curve_map.find(anim_uid) == curve_map.end()) { - fprintf(stderr, "Cannot find FCurve by animation UID.\n"); - continue; - } - - std::vector<FCurve*>& fcurves = curve_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, &animated); - } - else if (COLLADABU::Math::Vector3::UNIT_Y == axis) { - add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated); - } - else if (COLLADABU::Math::Vector3::UNIT_Z == axis) { - add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated); - } - break; - case COLLADAFW::AnimationList::AXISANGLE: - // TODO convert axis-angle to quat? or XYZ? - default: - fprintf(stderr, "AnimationClass %d is not supported for ROTATE 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; - } -#endif - - 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) { - copy_m4_m4(ob->obmat, mat); - object_apply_mat4(ob, ob->obmat); - } - } - -#if 0 - virtual void change_eul_to_quat(Object *ob, bAction *act) - { - bActionGroup *grp; - int i; - - for (grp = (bActionGroup*)act->groups.first; grp; grp = grp->next) { - - FCurve *eulcu[3] = {NULL, NULL, NULL}; - - if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) - continue; - - std::vector<FCurve*> &rot_fcurves = fcurves_actionGroup_map[grp]; - - if (rot_fcurves.size() > 3) continue; - - for (i = 0; i < rot_fcurves.size(); i++) - eulcu[rot_fcurves[i]->array_index] = rot_fcurves[i]; - - char joint_path[100]; - char rna_path[100]; - - BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name); - BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path); - - FCurve *quatcu[4] = { - create_fcurve(0, rna_path), - create_fcurve(1, rna_path), - create_fcurve(2, rna_path), - create_fcurve(3, rna_path) - }; - - bPoseChannel *chan = get_pose_channel(ob->pose, grp->name); - - float m4[4][4], irest[3][3]; - invert_m4_m4(m4, chan->bone->arm_mat); - copy_m3_m4(irest, m4); - - for (i = 0; i < 3; i++) { - - FCurve *cu = eulcu[i]; - - if (!cu) continue; - - for (int j = 0; j < cu->totvert; j++) { - float frame = cu->bezt[j].vec[1][0]; - - float eul[3] = { - eulcu[0] ? evaluate_fcurve(eulcu[0], frame) : 0.0f, - eulcu[1] ? evaluate_fcurve(eulcu[1], frame) : 0.0f, - eulcu[2] ? evaluate_fcurve(eulcu[2], frame) : 0.0f - }; - - // make eul relative to bone rest pose - float rot[3][3], rel[3][3], quat[4]; - - /*eul_to_mat3(rot, eul); - - mul_m3_m3m3(rel, irest, rot); - - mat3_to_quat(quat, rel); - */ - - eul_to_quat(quat, eul); - - for (int k = 0; k < 4; k++) - create_bezt(quatcu[k], frame, quat[k]); - } - } - - // now replace old Euler curves - - for (i = 0; i < 3; i++) { - if (!eulcu[i]) continue; - - action_groups_remove_channel(act, eulcu[i]); - free_fcurve(eulcu[i]); - } - - chan->rotmode = ROT_MODE_QUAT; - - for (i = 0; i < 4; i++) - action_groups_add_channel(act, grp, quatcu[i]); - } - - bPoseChannel *pchan; - for (pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) { - pchan->rotmode = ROT_MODE_QUAT; - } - } -#endif - - // prerequisites: - // animlist_map - map animlist id -> animlist - // curve_map - map anim id -> curve(s) - Object *translate_animation(COLLADAFW::Node *node, - std::map<COLLADAFW::UniqueId, Object*>& object_map, - std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& root_map, - COLLADAFW::Transformation::TransformationType tm_type, - Object *par_job = NULL) - { - bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; - bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; - bool is_joint = node->getType() == COLLADAFW::Node::JOINT; - - COLLADAFW::Node *root = root_map.find(node->getUniqueId()) == root_map.end() ? node : root_map[node->getUniqueId()]; - Object *ob = is_joint ? armature_importer->get_armature_for_joint(node) : object_map[node->getUniqueId()]; - const char *bone_name = is_joint ? get_joint_name(node) : NULL; - - if (!ob) { - fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); - return NULL; - } - - // frames at which to sample - std::vector<float> frames; - - // for each <rotate>, <translate>, etc. there is a separate Transformation - const COLLADAFW::TransformationPointerArray& tms = node->getTransformations(); - - unsigned int i; - - // find frames at which to sample plus convert all rotation keys to radians - for (i = 0; i < tms.getCount(); i++) { - COLLADAFW::Transformation *tm = tms[i]; - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - - if (type == tm_type) { - const COLLADAFW::UniqueId& listid = tm->getAnimationList(); - - if (animlist_map.find(listid) != animlist_map.end()) { - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings(); - - if (bindings.getCount()) { - for (unsigned int j = 0; j < bindings.getCount(); j++) { - std::vector<FCurve*>& curves = curve_map[bindings[j].animation]; - bool xyz = ((type == COLLADAFW::Transformation::TRANSLATE || type == COLLADAFW::Transformation::SCALE) && bindings[j].animationClass == COLLADAFW::AnimationList::POSITION_XYZ); - - if ((!xyz && curves.size() == 1) || (xyz && curves.size() == 3) || is_matrix) { - std::vector<FCurve*>::iterator iter; - - for (iter = curves.begin(); iter != curves.end(); iter++) { - FCurve *fcu = *iter; - - if (is_rotation) - fcurve_deg_to_rad(fcu); - - for (unsigned int k = 0; k < fcu->totvert; k++) { - float fra = fcu->bezt[k].vec[1][0]; - if (std::find(frames.begin(), frames.end(), fra) == frames.end()) - frames.push_back(fra); - } - } - } - else { - fprintf(stderr, "expected %d curves, got %u\n", xyz ? 3 : 1, curves.size()); - } - } - } - } - } - } - - float irest_dae[4][4]; - float rest[4][4], irest[4][4]; - - if (is_joint) { - get_joint_rest_mat(irest_dae, root, node); - invert_m4(irest_dae); - - Bone *bone = get_named_bone((bArmature*)ob->data, bone_name); - if (!bone) { - fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); - return NULL; - } - - unit_m4(rest); - copy_m4_m4(rest, bone->arm_mat); - invert_m4_m4(irest, rest); - } - - Object *job = NULL; - -#ifdef ARMATURE_TEST - FCurve *job_curves[10]; - job = get_joint_object(root, node, par_job); -#endif - - if (frames.size() == 0) - return job; - - std::sort(frames.begin(), frames.end()); - - const char *tm_str = NULL; - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - tm_str = "rotation_quaternion"; - break; - case COLLADAFW::Transformation::SCALE: - tm_str = "scale"; - break; - case COLLADAFW::Transformation::TRANSLATE: - tm_str = "location"; - break; - case COLLADAFW::Transformation::MATRIX: - break; - default: - return job; - } - - char rna_path[200]; - char joint_path[200]; - - if (is_joint) - armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); - - // new curves - FCurve *newcu[10]; // if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale - unsigned int totcu = is_matrix ? 10 : (is_rotation ? 4 : 3); - - for (i = 0; i < totcu; i++) { - - int axis = i; - - if (is_matrix) { - if (i < 4) { - tm_str = "rotation_quaternion"; - axis = i; - } - else if (i < 7) { - tm_str = "location"; - axis = i - 4; - } - else { - tm_str = "scale"; - axis = i - 7; - } - } - - if (is_joint) - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); - else - strcpy(rna_path, tm_str); - - newcu[i] = create_fcurve(axis, rna_path); - -#ifdef ARMATURE_TEST - if (is_joint) - job_curves[i] = create_fcurve(axis, tm_str); -#endif - } - - std::vector<float>::iterator it; - - // sample values at each frame - for (it = frames.begin(); it != frames.end(); it++) { - float fra = *it; - - float mat[4][4]; - float matfra[4][4]; +#include "collada_utils.h" - unit_m4(matfra); - - // calc object-space mat - evaluate_transform_at_frame(matfra, node, fra); - - // for joints, we need a special matrix - if (is_joint) { - // special matrix: iR * M * iR_dae * R - // where R, iR are bone rest and inverse rest mats in world space (Blender bones), - // iR_dae is joint inverse rest matrix (DAE) and M is an evaluated joint world-space matrix (DAE) - float temp[4][4], par[4][4]; - - // calc M - calc_joint_parent_mat_rest(par, NULL, root, node); - mul_m4_m4m4(temp, matfra, par); - - // evaluate_joint_world_transform_at_frame(temp, NULL, , node, fra); - - // calc special matrix - mul_serie_m4(mat, irest, temp, irest_dae, rest, NULL, NULL, NULL, NULL); - } - else { - copy_m4_m4(mat, matfra); - } - - float val[4], rot[4], loc[3], scale[3]; - - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - mat4_to_quat(val, mat); - break; - case COLLADAFW::Transformation::SCALE: - mat4_to_size(val, mat); - break; - case COLLADAFW::Transformation::TRANSLATE: - copy_v3_v3(val, mat[3]); - break; - case COLLADAFW::Transformation::MATRIX: - mat4_to_quat(rot, mat); - copy_v3_v3(loc, mat[3]); - mat4_to_size(scale, mat); - break; - default: - break; - } - - // add keys - for (i = 0; i < totcu; i++) { - if (is_matrix) { - if (i < 4) - add_bezt(newcu[i], fra, rot[i]); - else if (i < 7) - add_bezt(newcu[i], fra, loc[i - 4]); - else - add_bezt(newcu[i], fra, scale[i - 7]); - } - else { - add_bezt(newcu[i], fra, val[i]); - } - } - -#ifdef ARMATURE_TEST - if (is_joint) { - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - mat4_to_quat(val, matfra); - break; - case COLLADAFW::Transformation::SCALE: - mat4_to_size(val, matfra); - break; - case COLLADAFW::Transformation::TRANSLATE: - copy_v3_v3(val, matfra[3]); - break; - case MATRIX: - mat4_to_quat(rot, matfra); - copy_v3_v3(loc, matfra[3]); - mat4_to_size(scale, matfra); - break; - default: - break; - } - - for (i = 0; i < totcu; i++) { - if (is_matrix) { - if (i < 4) - add_bezt(job_curves[i], fra, rot[i]); - else if (i < 7) - add_bezt(job_curves[i], fra, loc[i - 4]); - else - add_bezt(job_curves[i], fra, scale[i - 7]); - } - else { - add_bezt(job_curves[i], fra, val[i]); - } - } - } -#endif - } - - verify_adt_action((ID*)&ob->id, 1); - - ListBase *curves = &ob->adt->action->curves; - - // add curves - for (i = 0; i < totcu; i++) { - if (is_joint) - add_bone_fcurve(ob, node, newcu[i]); - else - BLI_addtail(curves, newcu[i]); - -#ifdef ARMATURE_TEST - if (is_joint) - BLI_addtail(&job->adt->action->curves, job_curves[i]); -#endif - } - - if (is_rotation || is_matrix) { - if (is_joint) { - bPoseChannel *chan = get_pose_channel(ob->pose, bone_name); - chan->rotmode = ROT_MODE_QUAT; - } - else { - ob->rotmode = ROT_MODE_QUAT; - } - } - - return job; - } - - // internal, better make it private - // warning: evaluates only rotation - // prerequisites: animlist_map, curve_map - void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra) - { - const COLLADAFW::TransformationPointerArray& tms = node->getTransformations(); - - unit_m4(mat); - - for (unsigned int i = 0; i < tms.getCount(); i++) { - COLLADAFW::Transformation *tm = tms[i]; - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - float m[4][4]; - - unit_m4(m); - - if (!evaluate_animation(tm, m, fra, node->getOriginalId().c_str())) { - switch (type) { - case COLLADAFW::Transformation::ROTATE: - dae_rotate_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::TRANSLATE: - dae_translate_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::SCALE: - dae_scale_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::MATRIX: - dae_matrix_to_mat4(tm, m); - break; - default: - fprintf(stderr, "unsupported transformation type %d\n", type); - } - } - - float temp[4][4]; - copy_m4_m4(temp, mat); - - mul_m4_m4m4(mat, m, temp); - } - } - - // return true to indicate that mat contains a sane value - bool evaluate_animation(COLLADAFW::Transformation *tm, float mat[4][4], float fra, const char *node_id) - { - const COLLADAFW::UniqueId& listid = tm->getAnimationList(); - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - - if (type != COLLADAFW::Transformation::ROTATE && - type != COLLADAFW::Transformation::SCALE && - type != COLLADAFW::Transformation::TRANSLATE && - type != COLLADAFW::Transformation::MATRIX) { - fprintf(stderr, "animation of transformation %d is not supported yet\n", type); - return false; - } - - if (animlist_map.find(listid) == animlist_map.end()) - return false; - - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings& bindings = animlist->getAnimationBindings(); - - if (bindings.getCount()) { - float vec[3]; - - bool is_scale = (type == COLLADAFW::Transformation::SCALE); - bool is_translate = (type == COLLADAFW::Transformation::TRANSLATE); - - if (type == COLLADAFW::Transformation::SCALE) - dae_scale_to_v3(tm, vec); - else if (type == COLLADAFW::Transformation::TRANSLATE) - dae_translate_to_v3(tm, vec); - - for (unsigned int j = 0; j < bindings.getCount(); j++) { - const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[j]; - std::vector<FCurve*>& curves = curve_map[binding.animation]; - COLLADAFW::AnimationList::AnimationClass animclass = binding.animationClass; - char path[100]; - - switch (type) { - case COLLADAFW::Transformation::ROTATE: - BLI_snprintf(path, sizeof(path), "%s.rotate (binding %u)", node_id, j); - break; - case COLLADAFW::Transformation::SCALE: - BLI_snprintf(path, sizeof(path), "%s.scale (binding %u)", node_id, j); - break; - case COLLADAFW::Transformation::TRANSLATE: - BLI_snprintf(path, sizeof(path), "%s.translate (binding %u)", node_id, j); - break; - case COLLADAFW::Transformation::MATRIX: - BLI_snprintf(path, sizeof(path), "%s.matrix (binding %u)", node_id, j); - break; - default: - break; - } - - if (animclass == COLLADAFW::AnimationList::UNKNOWN_CLASS) { - fprintf(stderr, "%s: UNKNOWN animation class\n", path); - continue; - } - - if (type == COLLADAFW::Transformation::ROTATE) { - if (curves.size() != 1) { - fprintf(stderr, "expected 1 curve, got %u\n", curves.size()); - return false; - } - - // TODO support other animclasses - if (animclass != COLLADAFW::AnimationList::ANGLE) { - fprintf(stderr, "%s: animation class %d is not supported yet\n", path, animclass); - return false; - } - - COLLADABU::Math::Vector3& axis = ((COLLADAFW::Rotate*)tm)->getRotationAxis(); - float ax[3] = {axis[0], axis[1], axis[2]}; - float angle = evaluate_fcurve(curves[0], fra); - axis_angle_to_mat4(mat, ax, angle); - - return true; - } - else if (is_scale || is_translate) { - bool is_xyz = animclass == COLLADAFW::AnimationList::POSITION_XYZ; - - if ((!is_xyz && curves.size() != 1) || (is_xyz && curves.size() != 3)) { - if (is_xyz) - fprintf(stderr, "%s: expected 3 curves, got %u\n", path, curves.size()); - else - fprintf(stderr, "%s: expected 1 curve, got %u\n", path, curves.size()); - return false; - } - - switch (animclass) { - case COLLADAFW::AnimationList::POSITION_X: - vec[0] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_Y: - vec[1] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_Z: - vec[2] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_XYZ: - vec[0] = evaluate_fcurve(curves[0], fra); - vec[1] = evaluate_fcurve(curves[1], fra); - vec[2] = evaluate_fcurve(curves[2], fra); - break; - default: - fprintf(stderr, "%s: animation class %d is not supported yet\n", path, animclass); - break; - } - } - else if (type == COLLADAFW::Transformation::MATRIX) { - // for now, of matrix animation, support only the case when all values are packed into one animation - if (curves.size() != 16) { - fprintf(stderr, "%s: expected 16 curves, got %u\n", path, curves.size()); - return false; - } - - COLLADABU::Math::Matrix4 matrix; - int i = 0, j = 0; - - for (std::vector<FCurve*>::iterator it = curves.begin(); it != curves.end(); it++) { - matrix.setElement(i, j, evaluate_fcurve(*it, fra)); - j++; - if (j == 4) { - i++; - j = 0; - } - } - - COLLADAFW::Matrix tm(matrix); - dae_matrix_to_mat4(&tm, mat); - - return true; - } - } - - if (is_scale) - size_to_mat4(mat, vec); - else - copy_v3_v3(mat[3], vec); - - return is_scale || is_translate; - } - - return false; - } - - // gives a world-space mat of joint at rest position - void get_joint_rest_mat(float mat[4][4], COLLADAFW::Node *root, COLLADAFW::Node *node) - { - // if bind mat is not available, - // use "current" node transform, i.e. all those tms listed inside <node> - if (!armature_importer->get_joint_bind_mat(mat, node)) { - float par[4][4], m[4][4]; - - calc_joint_parent_mat_rest(par, NULL, root, node); - get_node_mat(m, node, NULL, NULL); - mul_m4_m4m4(mat, m, par); - } - } - - // gives a world-space mat, end's mat not included - bool calc_joint_parent_mat_rest(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end) - { - float m[4][4]; - - if (node == end) { - par ? copy_m4_m4(mat, par) : unit_m4(mat); - return true; - } - - // use bind matrix if available or calc "current" world mat - if (!armature_importer->get_joint_bind_mat(m, node)) { - if (par) { - float temp[4][4]; - get_node_mat(temp, node, NULL, NULL); - mul_m4_m4m4(m, temp, par); - } - else { - get_node_mat(m, node, NULL, NULL); - } - } - - COLLADAFW::NodePointerArray& children = node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (calc_joint_parent_mat_rest(mat, m, children[i], end)) - return true; - } - - return false; - } - -#ifdef ARMATURE_TEST - Object *get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job) - { - if (joint_objects.find(node->getUniqueId()) == joint_objects.end()) { - Object *job = add_object(scene, OB_EMPTY); - - rename_id((ID*)&job->id, (char*)get_joint_name(node)); - - job->lay = object_in_scene(job, scene)->lay = 2; - - mul_v3_fl(job->size, 0.5f); - job->recalc |= OB_RECALC_OB; - - verify_adt_action((ID*)&job->id, 1); - - job->rotmode = ROT_MODE_QUAT; - - float mat[4][4]; - get_joint_rest_mat(mat, root, node); - - if (par_job) { - float temp[4][4], ipar[4][4]; - invert_m4_m4(ipar, par_job->obmat); - copy_m4_m4(temp, mat); - mul_m4_m4m4(mat, temp, ipar); - } - - TransformBase::decompose(mat, job->loc, NULL, job->quat, job->size); - - if (par_job) { - job->parent = par_job; - - par_job->recalc |= OB_RECALC_OB; - job->parsubstr[0] = 0; - } - - where_is_object(scene, job); - - // after parenting and layer change - DAG_scene_sort(CTX_data_main(C), scene); - - joint_objects[node->getUniqueId()] = job; - } - - return joint_objects[node->getUniqueId()]; - } -#endif - -#if 0 - // recursively evaluates joint tree until end is found, mat then is world-space matrix of end - // mat must be identity on enter, node must be root - bool evaluate_joint_world_transform_at_frame(float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra) - { - float m[4][4]; - if (par) { - float temp[4][4]; - evaluate_transform_at_frame(temp, node, node == end ? fra : 0.0f); - mul_m4_m4m4(m, temp, par); - } - else { - evaluate_transform_at_frame(m, node, node == end ? fra : 0.0f); - } - - if (node == end) { - copy_m4_m4(mat, m); - return true; - } - else { - COLLADAFW::NodePointerArray& children = node->getChildNodes(); - for (int i = 0; i < children.getCount(); i++) { - if (evaluate_joint_world_transform_at_frame(mat, m, children[i], end, fra)) - return true; - } - } - - return false; - } -#endif - - void add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu) - { - const char *bone_name = get_joint_name(node); - bAction *act = ob->adt->action; - - /* try to find group */ - bActionGroup *grp = action_groups_find_named(act, bone_name); - - /* no matching groups, so add one */ - if (grp == NULL) { - /* Add a new group, and make it active */ - grp = (bActionGroup*)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); - - grp->flag = AGRP_SELECTED; - BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); - - BLI_addtail(&act->groups, grp); - BLI_uniquename(&act->groups, grp, "Group", '.', offsetof(bActionGroup, name), 64); - } - - /* add F-Curve to group */ - action_groups_add_channel(act, grp, fcu); - } - - void add_bezt(FCurve *fcu, float fra, float value) - { - BezTriple bez; - memset(&bez, 0, sizeof(BezTriple)); - bez.vec[1][0] = fra; - bez.vec[1][1] = value; - 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, 0); - calchandles_fcurve(fcu); - } -}; /* - COLLADA Importer limitations: - - no multiple scene import, all objects are added to active scene - */ + +// #define COLLADA_DEBUG +// creates empties for each imported bone on layer 2, for debugging +// #define ARMATURE_TEST + /** Class that needs to be implemented by a writer. IMPORTANT: The write functions are called in arbitrary order.*/ -class Writer: public COLLADAFW::IWriter -{ +/* private: std::string mFilename; @@ -3240,43 +104,58 @@ private: std::map<COLLADAFW::UniqueId, Lamp*> uid_lamp_map; std::map<Material*, TexIndexTextureArrayMap> material_texture_mapping_map; std::map<COLLADAFW::UniqueId, Object*> object_map; + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> node_map; std::vector<const COLLADAFW::VisualScene*> vscenes; + std::vector<Object*> libnode_ob; - std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> root_map; // find root joint by child joint uid, for bone tree evaluation during resampling + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> root_map; + */ + // find root joint by child joint uid, for bone tree evaluation during resampling // animation // std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map; // Nodes don't share AnimationLists (Arystan) // std::map<COLLADAFW::UniqueId, Animation> uid_animated_map; // AnimationList->uniqueId to AnimatedObject map -public: +//public: /** Constructor. */ - Writer(bContext *C, const char *filename) : mFilename(filename), mContext(C), - armature_importer(&unit_converter, &mesh_importer, &anim_importer, CTX_data_scene(C)), - mesh_importer(&armature_importer, CTX_data_scene(C)), - anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) {} + DocumentImporter::DocumentImporter(bContext *C, const char *filename) : + mImportStage(General), + mFilename(filename), + mContext(C), + armature_importer(&unit_converter, &mesh_importer, &anim_importer, CTX_data_scene(C)), + mesh_importer(&unit_converter, &armature_importer, CTX_data_scene(C)), + anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) + {} /** Destructor. */ - ~Writer() {} + DocumentImporter::~DocumentImporter() {} - bool write() + bool DocumentImporter::import() { + /** TODO Add error handler (implement COLLADASaxFWL::IErrorHandler */ COLLADASaxFWL::Loader loader; COLLADAFW::Root root(&loader, this); - // XXX report error if (!root.loadDocument(mFilename)) return false; + + /** TODO set up scene graph and such here */ + + mImportStage = Controller; + + COLLADASaxFWL::Loader loader2; + COLLADAFW::Root root2(&loader2, this); + + if (!root2.loadDocument(mFilename)) + return false; + return true; } - - /** This method will be called if an error in the loading process occurred and the loader cannot - continue to load. The writer should undo all operations that have been performed. - @param errorMessage A message containing informations about the error that occurred. - */ - virtual void cancel(const COLLADAFW::String& errorMessage) + + void DocumentImporter::cancel(const COLLADAFW::String& errorMessage) { // TODO: if possible show error info // @@ -3286,14 +165,50 @@ public: // The latter sounds better. } - /** This is the method called. The writer hast to prepare to receive data.*/ - virtual void start() - { - } + void DocumentImporter::start(){} /** This method is called after the last write* method. No other methods will be called after this.*/ - virtual void finish() + void DocumentImporter::finish() { + if(mImportStage!=General) + return; + + /** TODO Break up and put into 2-pass parsing of DAE */ + std::vector<const COLLADAFW::VisualScene*>::iterator it; + for (it = vscenes.begin(); it != vscenes.end(); it++) { + PointerRNA sceneptr, unit_settings; + PropertyRNA *system, *scale; + // TODO: create a new scene except the selected <visual_scene> - use current blender scene for it + Scene *sce = CTX_data_scene(mContext); + + // for scene unit settings: system, scale_length + RNA_id_pointer_create(&sce->id, &sceneptr); + unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); + system = RNA_struct_find_property(&unit_settings, "system"); + scale = RNA_struct_find_property(&unit_settings, "scale_length"); + + switch(unit_converter.isMetricSystem()) { + case UnitConverter::Metric: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_METRIC); + break; + case UnitConverter::Imperial: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_IMPERIAL); + break; + default: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_NONE); + break; + } + RNA_property_float_set(&unit_settings, scale, unit_converter.getLinearMeter()); + + const COLLADAFW::NodePointerArray& roots = (*it)->getRootNodes(); + + for (unsigned int i = 0; i < roots.getCount(); i++) { + write_node(roots[i], NULL, sce, NULL, false); + } + } + + armature_importer.make_armatures(mContext); + #if 0 armature_importer.fix_animation(); #endif @@ -3302,13 +217,36 @@ public: const COLLADAFW::NodePointerArray& roots = (*it)->getRootNodes(); for (unsigned int i = 0; i < roots.getCount(); i++) - translate_anim_recursive(roots[i]); + translate_anim_recursive(roots[i],NULL,NULL); } + if (libnode_ob.size()) { + Scene *sce = CTX_data_scene(mContext); + + fprintf(stderr, "got %d library nodes to free\n", (int)libnode_ob.size()); + // free all library_nodes + std::vector<Object*>::iterator it; + for (it = libnode_ob.begin(); it != libnode_ob.end(); it++) { + Object *ob = *it; + + Base *base = object_in_scene(ob, sce); + if (base) { + BLI_remlink(&sce->base, base); + free_libblock_us(&G.main->object, base->object); + if (sce->basact==base) + sce->basact= NULL; + MEM_freeN(base); + } + } + libnode_ob.clear(); + + DAG_scene_sort(CTX_data_main(mContext), sce); + DAG_ids_flush_update(CTX_data_main(mContext), 0); + } } - void translate_anim_recursive(COLLADAFW::Node *node, COLLADAFW::Node *par = NULL, Object *parob = NULL) + void DocumentImporter::translate_anim_recursive(COLLADAFW::Node *node, COLLADAFW::Node *par = NULL, Object *parob = NULL) { if (par && par->getType() == COLLADAFW::Node::JOINT) { // par is root if there's no corresp. key in root_map @@ -3339,11 +277,8 @@ public: /** When this method is called, the writer must write the global document asset. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeGlobalAsset ( const COLLADAFW::FileInfo* asset ) + bool DocumentImporter::writeGlobalAsset ( const COLLADAFW::FileInfo* asset ) { - // XXX take up_axis, unit into account - // COLLADAFW::FileInfo::Unit unit = asset->getUnit(); - // COLLADAFW::FileInfo::UpAxisType upAxis = asset->getUpAxisType(); unit_converter.read_asset(asset); return true; @@ -3351,44 +286,86 @@ public: /** When this method is called, the writer must write the scene. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeScene ( const COLLADAFW::Scene* scene ) + bool DocumentImporter::writeScene ( const COLLADAFW::Scene* scene ) { // XXX could store the scene id, but do nothing for now return true; } - Object *create_camera_object(COLLADAFW::InstanceCamera *camera, Object *ob, Scene *sce) + Object* DocumentImporter::create_camera_object(COLLADAFW::InstanceCamera *camera, Scene *sce) { const COLLADAFW::UniqueId& cam_uid = camera->getInstanciatedObjectId(); if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) { - fprintf(stderr, "Couldn't find camera by UID. \n"); + fprintf(stderr, "Couldn't find camera by UID.\n"); return NULL; } - ob = add_object(sce, OB_CAMERA); + Object *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); + old_cam->id.us--; + if (old_cam->id.us == 0) + free_libblock(&G.main->camera, old_cam); return ob; } - Object *create_lamp_object(COLLADAFW::InstanceLight *lamp, Object *ob, Scene *sce) + Object* DocumentImporter::create_lamp_object(COLLADAFW::InstanceLight *lamp, Scene *sce) { const COLLADAFW::UniqueId& lamp_uid = lamp->getInstanciatedObjectId(); if (uid_lamp_map.find(lamp_uid) == uid_lamp_map.end()) { fprintf(stderr, "Couldn't find lamp by UID. \n"); return NULL; } - ob = add_object(sce, OB_LAMP); + Object *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); + old_lamp->id.us--; + if (old_lamp->id.us == 0) + free_libblock(&G.main->lamp, old_lamp); return ob; } + + Object* DocumentImporter::create_instance_node(Object *source_ob, COLLADAFW::Node *source_node, COLLADAFW::Node *instance_node, Scene *sce, bool is_library_node) + { + Object *obn = copy_object(source_ob); + obn->recalc |= OB_RECALC_OB|OB_RECALC_DATA|OB_RECALC_TIME; + scene_add_base(sce, obn); + + if (instance_node) + anim_importer.read_node_transform(instance_node, obn); + else + anim_importer.read_node_transform(source_node, obn); + + DAG_scene_sort(CTX_data_main(mContext), sce); + DAG_ids_flush_update(CTX_data_main(mContext), 0); + + COLLADAFW::NodePointerArray &children = source_node->getChildNodes(); + if (children.getCount()) { + for (unsigned int i = 0; i < children.getCount(); i++) { + COLLADAFW::Node *child_node = children[i]; + const COLLADAFW::UniqueId& child_id = child_node->getUniqueId(); + if (object_map.find(child_id) == object_map.end()) + continue; + COLLADAFW::InstanceNodePointerArray &inodes = child_node->getInstanceNodes(); + Object *new_child = NULL; + if (inodes.getCount()) { + const COLLADAFW::UniqueId& id = inodes[0]->getInstanciatedObjectId(); + new_child = create_instance_node(object_map[id], node_map[id], child_node, sce, is_library_node); + } + else { + new_child = create_instance_node(object_map[child_id], child_node, NULL, sce, is_library_node); + } + bc_set_parent(new_child, obn, mContext, true); + + if (is_library_node) + libnode_ob.push_back(new_child); + } + } + + return obn; + } - void write_node (COLLADAFW::Node *node, COLLADAFW::Node *parent_node, Scene *sce, Object *par) + void DocumentImporter::write_node (COLLADAFW::Node *node, COLLADAFW::Node *parent_node, Scene *sce, Object *par, bool is_library_node) { Object *ob = NULL; bool is_joint = node->getType() == COLLADAFW::Node::JOINT; @@ -3402,39 +379,64 @@ public: COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights(); COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers(); COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes(); + int geom_done = 0; + int camera_done = 0; + int lamp_done = 0; + int controller_done = 0; + int inst_done = 0; // 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, + while (geom_done < geom.getCount()) { + ob = mesh_importer.create_mesh_object(node, geom[geom_done], false, uid_material_map, material_texture_mapping_map); + ++geom_done; } - else if (camera.getCount() != 0) { - ob = create_camera_object(camera[0], ob, sce); + while (camera_done < camera.getCount()) { + ob = create_camera_object(camera[camera_done], sce); + ++camera_done; } - else if (lamp.getCount() != 0) { - ob = create_lamp_object(lamp[0], ob, sce); + while (lamp_done < lamp.getCount()) { + ob = create_lamp_object(lamp[lamp_done], sce); + ++lamp_done; } - else if (controller.getCount() != 0) { - COLLADAFW::InstanceGeometry *geom = (COLLADAFW::InstanceGeometry*)controller[0]; + while (controller_done < controller.getCount()) { + COLLADAFW::InstanceGeometry *geom = (COLLADAFW::InstanceGeometry*)controller[controller_done]; ob = mesh_importer.create_mesh_object(node, geom, true, uid_material_map, material_texture_mapping_map); + ++controller_done; } // XXX instance_node is not supported yet - else if (inst_node.getCount() != 0) { - return; + while (inst_done < inst_node.getCount()) { + const COLLADAFW::UniqueId& node_id = inst_node[inst_done]->getInstanciatedObjectId(); + if (object_map.find(node_id) == object_map.end()) { + fprintf(stderr, "Cannot find node to instanciate.\n"); + ob = NULL; + } + else { + Object *source_ob = object_map[node_id]; + COLLADAFW::Node *source_node = node_map[node_id]; + + ob = create_instance_node(source_ob, source_node, node, sce, is_library_node); + } + ++inst_done; } // if node is empty - create empty object // XXX empty node may not mean it is empty object, not sure about this - else { + if ( (geom_done + camera_done + lamp_done + controller_done + inst_done) < 1) { ob = add_object(sce, OB_EMPTY); - rename_id(&ob->id, (char*)node->getOriginalId().c_str()); } // check if object is not NULL if (!ob) return; + + rename_id(&ob->id, (char*)node->getOriginalId().c_str()); - object_map[node->getUniqueId()] = ob; + object_map[node->getUniqueId()] = ob; + node_map[node->getUniqueId()] = node; + + if (is_library_node) + libnode_ob.push_back(ob); } anim_importer.read_node_transform(node, ob); @@ -3442,20 +444,23 @@ public: if (!is_joint) { // if par was given make this object child of the previous if (par && ob) - set_parent(ob, par, mContext); + bc_set_parent(ob, par, mContext); } // if node has child nodes write them COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes(); for (unsigned int i = 0; i < child_nodes.getCount(); i++) { - write_node(child_nodes[i], node, sce, ob); + write_node(child_nodes[i], node, sce, ob, is_library_node); } } /** When this method is called, the writer must write the entire visual scene. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeVisualScene ( const COLLADAFW::VisualScene* visualScene ) + bool DocumentImporter::writeVisualScene ( const COLLADAFW::VisualScene* visualScene ) { + if(mImportStage!=General) + return true; + // this method called on post process after writeGeometry, writeMaterial, etc. // for each <node> in <visual_scene>: @@ -3467,17 +472,6 @@ public: // we link Objects with Meshes here vscenes.push_back(visualScene); - - // TODO: create a new scene except the selected <visual_scene> - use current blender - // scene for it - Scene *sce = CTX_data_scene(mContext); - const COLLADAFW::NodePointerArray& roots = visualScene->getRootNodes(); - - for (unsigned int i = 0; i < roots.getCount(); i++) { - write_node(roots[i], NULL, sce, NULL); - } - - armature_importer.make_armatures(mContext); return true; } @@ -3485,22 +479,39 @@ public: /** When this method is called, the writer must handle all nodes contained in the library nodes. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeLibraryNodes ( const COLLADAFW::LibraryNodes* libraryNodes ) + bool DocumentImporter::writeLibraryNodes ( const COLLADAFW::LibraryNodes* libraryNodes ) { + if(mImportStage!=General) + return true; + + Scene *sce = CTX_data_scene(mContext); + + const COLLADAFW::NodePointerArray& nodes = libraryNodes->getNodes(); + + for (unsigned int i = 0; i < nodes.getCount(); i++) { + write_node(nodes[i], NULL, sce, NULL, true); + } + return true; } /** When this method is called, the writer must write the geometry. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeGeometry ( const COLLADAFW::Geometry* geom ) + bool DocumentImporter::writeGeometry ( const COLLADAFW::Geometry* geom ) { + if(mImportStage!=General) + return true; + return mesh_importer.write_geometry(geom); } /** When this method is called, the writer must write the material. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeMaterial( const COLLADAFW::Material* cmat ) + bool DocumentImporter::writeMaterial( const COLLADAFW::Material* cmat ) { + if(mImportStage!=General) + return true; + const std::string& str_mat_id = cmat->getOriginalId(); Material *ma = add_material((char*)str_mat_id.c_str()); @@ -3511,7 +522,7 @@ public: } // create mtex, create texture, set texture image - MTex *create_texture(COLLADAFW::EffectCommon *ef, COLLADAFW::Texture &ctex, Material *ma, + MTex* DocumentImporter::create_texture(COLLADAFW::EffectCommon *ef, COLLADAFW::Texture &ctex, Material *ma, int i, TexIndexTextureArrayMap &texindex_texarray_map) { COLLADAFW::SamplerPointerArray& samp_array = ef->getSamplerPointerArray(); @@ -3536,7 +547,7 @@ public: return ma->mtex[i]; } - void write_profile_COMMON(COLLADAFW::EffectCommon *ef, Material *ma) + void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Material *ma) { COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType(); @@ -3548,8 +559,7 @@ public: // phong else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) { ma->spec_shader = MA_SPEC_PHONG; - // XXX setting specular hardness instead of specularity intensity - ma->har = ef->getShininess().getFloatValue() * 4; + ma->har = ef->getShininess().getFloatValue(); } // lambert else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) { @@ -3674,8 +684,10 @@ public: /** When this method is called, the writer must write the effect. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeEffect( const COLLADAFW::Effect* effect ) + bool DocumentImporter::writeEffect( const COLLADAFW::Effect* effect ) { + if(mImportStage!=General) + return true; const COLLADAFW::UniqueId& uid = effect->getUniqueId(); if (uid_effect_map.find(uid) == uid_effect_map.end()) { @@ -3701,8 +713,11 @@ public: /** When this method is called, the writer must write the camera. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeCamera( const COLLADAFW::Camera* camera ) + bool DocumentImporter::writeCamera( const COLLADAFW::Camera* camera ) { + if(mImportStage!=General) + return true; + Camera *cam = NULL; std::string cam_id, cam_name; @@ -3737,6 +752,75 @@ public: } break; } + + switch(camera->getDescriptionType()) { + case COLLADAFW::Camera::ASPECTRATIO_AND_Y: + { + switch(cam->type) { + case CAM_ORTHO: + { + double ymag = camera->getYMag().getValue(); + double aspect = camera->getAspectRatio().getValue(); + double xmag = aspect*ymag; + cam->ortho_scale = (float)xmag; + } + break; + case CAM_PERSP: + default: + { + double yfov = camera->getYFov().getValue(); + double aspect = camera->getAspectRatio().getValue(); + double xfov = aspect*yfov; + // xfov is in degrees, cam->lens is in millimiters + cam->lens = angle_to_lens((float)xfov*(M_PI/180.0f)); + } + break; + } + } + break; + /* XXX correct way to do following four is probably to get also render + size and determine proper settings from that somehow */ + case COLLADAFW::Camera::ASPECTRATIO_AND_X: + case COLLADAFW::Camera::SINGLE_X: + case COLLADAFW::Camera::X_AND_Y: + { + switch(cam->type) { + case CAM_ORTHO: + cam->ortho_scale = (float)camera->getXMag().getValue(); + break; + case CAM_PERSP: + default: + { + double x = camera->getXFov().getValue(); + // x is in degrees, cam->lens is in millimiters + cam->lens = angle_to_lens((float)x*(M_PI/180.0f)); + } + break; + } + } + break; + case COLLADAFW::Camera::SINGLE_Y: + { + switch(cam->type) { + case CAM_ORTHO: + cam->ortho_scale = (float)camera->getYMag().getValue(); + break; + case CAM_PERSP: + default: + { + double yfov = camera->getYFov().getValue(); + // yfov is in degrees, cam->lens is in millimiters + cam->lens = angle_to_lens((float)yfov*(M_PI/180.0f)); + } + break; + } + } + break; + case COLLADAFW::Camera::UNDEFINED: + // read nothing, use blender defaults. + break; + } + this->uid_camera_map[camera->getUniqueId()] = cam; // XXX import camera options return true; @@ -3744,8 +828,11 @@ public: /** When this method is called, the writer must write the image. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeImage( const COLLADAFW::Image* image ) + bool DocumentImporter::writeImage( const COLLADAFW::Image* image ) { + if(mImportStage!=General) + return true; + // XXX maybe it is necessary to check if the path is absolute or relative const std::string& filepath = image->getImageURI().toNativePath(); const char *filename = (const char*)mFilename.c_str(); @@ -3753,8 +840,8 @@ public: char full_path[FILE_MAX]; BLI_split_dirfile(filename, dir, NULL); - BLI_join_dirfile(full_path, dir, filepath.c_str()); - Image *ima = BKE_add_image_file(full_path, 0); + BLI_join_dirfile(full_path, sizeof(full_path), dir, filepath.c_str()); + Image *ima = BKE_add_image_file(full_path); if (!ima) { fprintf(stderr, "Cannot create image. \n"); return true; @@ -3766,8 +853,11 @@ public: /** When this method is called, the writer must write the light. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeLight( const COLLADAFW::Light* light ) + bool DocumentImporter::writeLight( const COLLADAFW::Light* light ) { + if(mImportStage!=General) + return true; + Lamp *lamp = NULL; std::string la_id, la_name; @@ -3786,6 +876,38 @@ public: lamp->g = col.getGreen(); lamp->b = col.getBlue(); } + float constatt = light->getConstantAttenuation().getValue(); + float linatt = light->getLinearAttenuation().getValue(); + float quadatt = light->getQuadraticAttenuation().getValue(); + float d = 25.0f; + float att1 = 0.0f; + float att2 = 0.0f; + + float e = 1.0f/constatt; + + /* NOTE: We assume for now that inv square is used for quadratic light + * and inv linear for linear light. Exported blender lin/quad weighted + * most likely will result in wrong import. */ + /* quadratic light */ + if(IS_EQ(linatt, 0.0f) && quadatt > 0.0f) { + //quadatt = att2/(d*d*(e*2)); + float invquadatt = 1.0f/quadatt; + float d2 = invquadatt / (2 * e); + d = sqrtf(d2); + } + // linear light + else if(IS_EQ(quadatt, 0.0f) && linatt > 0.0f) { + //linatt = att1/(d*e); + float invlinatt = 1.0f/linatt; + d = invlinatt / e; + } else { + printf("no linear nor quad light, using defaults for attenuation, import will be incorrect: Lamp %s\n", lamp->id.name); + att2 = 1.0f; + } + + lamp->dist = d; + lamp->energy = e; + COLLADAFW::Light::LightType type = light->getLightType(); switch(type) { case COLLADAFW::Light::AMBIENT_LIGHT: @@ -3796,9 +918,9 @@ public: case COLLADAFW::Light::SPOT_LIGHT: { lamp->type = LA_SPOT; - lamp->falloff_type = LA_FALLOFF_SLIDERS; - lamp->att1 = light->getLinearAttenuation().getValue(); - lamp->att2 = light->getQuadraticAttenuation().getValue(); + lamp->falloff_type = LA_FALLOFF_INVSQUARE; + lamp->att1 = att1; + lamp->att2 = att2; lamp->spotsize = light->getFallOffAngle().getValue(); lamp->spotblend = light->getFallOffExponent().getValue(); } @@ -3811,8 +933,9 @@ public: case COLLADAFW::Light::POINT_LIGHT: { lamp->type = LA_LOCAL; - lamp->att1 = light->getLinearAttenuation().getValue(); - lamp->att2 = light->getQuadraticAttenuation().getValue(); + lamp->falloff_type = LA_FALLOFF_INVSQUARE; + lamp->att1 = att1; + lamp->att2 = att2; } break; case COLLADAFW::Light::UNDEFINED: @@ -3828,45 +951,50 @@ public: } // this function is called only for animations that pass COLLADAFW::validate - virtual bool writeAnimation( const COLLADAFW::Animation* anim ) + bool DocumentImporter::writeAnimation( const COLLADAFW::Animation* anim ) { + if(mImportStage!=General) + return true; + // return true; return anim_importer.write_animation(anim); } // called on post-process stage after writeVisualScenes - virtual bool writeAnimationList( const COLLADAFW::AnimationList* animationList ) + bool DocumentImporter::writeAnimationList( const COLLADAFW::AnimationList* animationList ) { + if(mImportStage!=General) + return true; + // return true; return anim_importer.write_animation_list(animationList); } /** When this method is called, the writer must write the skin controller data. @return The writer should return true, if writing succeeded, false otherwise.*/ - virtual bool writeSkinControllerData( const COLLADAFW::SkinControllerData* skin ) + bool DocumentImporter::writeSkinControllerData( const COLLADAFW::SkinControllerData* skin ) { return armature_importer.write_skin_controller_data(skin); } // this is called on postprocess, before writeVisualScenes - virtual bool writeController( const COLLADAFW::Controller* controller ) + bool DocumentImporter::writeController( const COLLADAFW::Controller* controller ) { + if(mImportStage!=General) + return true; + return armature_importer.write_controller(controller); } - virtual bool writeFormulas( const COLLADAFW::Formulas* formulas ) + bool DocumentImporter::writeFormulas( const COLLADAFW::Formulas* formulas ) { return true; } - virtual bool writeKinematicsScene( const COLLADAFW::KinematicsScene* kinematicsScene ) + bool DocumentImporter::writeKinematicsScene( const COLLADAFW::KinematicsScene* kinematicsScene ) { return true; } -}; -void DocumentImporter::import(bContext *C, const char *filename) -{ - Writer w(C, filename); - w.write(); -} + + diff --git a/source/blender/collada/DocumentImporter.h b/source/blender/collada/DocumentImporter.h index babf8f65d7f..e312268c458 100644 --- a/source/blender/collada/DocumentImporter.h +++ b/source/blender/collada/DocumentImporter.h @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -21,11 +21,132 @@ * * ***** END GPL LICENSE BLOCK ***** */ + +/** \file DocumentImporter.h + * \ingroup collada + */ + +#ifndef __DOCUMENTIMPORTER_H__ +#define __DOCUMENTIMPORTER_H__ + +#include "COLLADAFWIWriter.h" +#include "COLLADAFWMaterial.h" +#include "COLLADAFWEffect.h" +#include "COLLADAFWColor.h" +#include "COLLADAFWImage.h" +#include "COLLADAFWInstanceGeometry.h" +#include "COLLADAFWController.h" +#include "COLLADAFWMorphController.h" +#include "COLLADAFWSkinController.h" + +#include "BKE_object.h" + +#include "TransformReader.h" +#include "AnimationImporter.h" +#include "ArmatureImporter.h" +#include "MeshImporter.h" + + struct Main; struct bContext; -class DocumentImporter +/** Importer class. */ +class DocumentImporter : COLLADAFW::IWriter { public: - void import(bContext *C, const char *filename); + //! Enumeration to denote the stage of import + enum ImportStage { + General, //!< First pass to collect all data except controller + Controller, //!< Second pass to collect controller data + }; + /** Constructor */ + DocumentImporter(bContext *C, const char *filename); + + /** Destructor */ + ~DocumentImporter(); + + /** Function called by blender UI */ + bool import(); + + /** these should not be here */ + Object* create_camera_object(COLLADAFW::InstanceCamera*, Scene*); + Object* create_lamp_object(COLLADAFW::InstanceLight*, Scene*); + Object* create_instance_node(Object*, COLLADAFW::Node*, COLLADAFW::Node*, Scene*, bool); + void write_node(COLLADAFW::Node*, COLLADAFW::Node*, Scene*, Object*, bool); + MTex* create_texture(COLLADAFW::EffectCommon*, COLLADAFW::Texture&, Material*, int, TexIndexTextureArrayMap&); + void write_profile_COMMON(COLLADAFW::EffectCommon*, Material*); + void translate_anim_recursive(COLLADAFW::Node*, COLLADAFW::Node*, Object*); + + /** This method will be called if an error in the loading process occurred and the loader cannot + continue to load. The writer should undo all operations that have been performed. + @param errorMessage A message containing informations about the error that occurred. + */ + void cancel(const COLLADAFW::String& errorMessage); + + /** This is the method called. The writer hast to prepare to receive data.*/ + void start(); + + /** This method is called after the last write* method. No other methods will be called after this.*/ + void finish(); + + bool writeGlobalAsset(const COLLADAFW::FileInfo*); + + bool writeScene(const COLLADAFW::Scene*); + + bool writeVisualScene(const COLLADAFW::VisualScene*); + + bool writeLibraryNodes(const COLLADAFW::LibraryNodes*); + + bool writeAnimation(const COLLADAFW::Animation*); + + bool writeAnimationList(const COLLADAFW::AnimationList*); + + bool writeGeometry(const COLLADAFW::Geometry*); + + bool writeMaterial(const COLLADAFW::Material*); + + bool writeEffect(const COLLADAFW::Effect*); + + bool writeCamera(const COLLADAFW::Camera*); + + bool writeImage(const COLLADAFW::Image*); + + bool writeLight(const COLLADAFW::Light*); + + bool writeSkinControllerData(const COLLADAFW::SkinControllerData*); + + bool writeController(const COLLADAFW::Controller*); + + bool writeFormulas(const COLLADAFW::Formulas*); + + bool writeKinematicsScene(const COLLADAFW::KinematicsScene*); + + private: + + /** Current import stage we're in. */ + ImportStage mImportStage; + std::string mFilename; + + bContext *mContext; + + 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; + std::map<COLLADAFW::UniqueId, Material*> uid_effect_map; + std::map<COLLADAFW::UniqueId, Camera*> uid_camera_map; + std::map<COLLADAFW::UniqueId, Lamp*> uid_lamp_map; + std::map<Material*, TexIndexTextureArrayMap> material_texture_mapping_map; + std::map<COLLADAFW::UniqueId, Object*> object_map; + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> node_map; + std::vector<const COLLADAFW::VisualScene*> vscenes; + std::vector<Object*> libnode_ob; + + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> root_map; // find root joint by child joint uid, for bone tree evaluation during resampling + }; + +#endif diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp new file mode 100644 index 00000000000..d20cb74ba9f --- /dev/null +++ b/source/blender/collada/EffectExporter.cpp @@ -0,0 +1,306 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <map> + +#include "COLLADASWEffectProfile.h" + +#include "EffectExporter.h" +#include "MaterialExporter.h" + +#include "DNA_mesh_types.h" +#include "DNA_texture_types.h" + +#include "BKE_customdata.h" + +#include "collada_internal.h" +#include "collada_utils.h" + +// OB_MESH is assumed +static std::string getActiveUVLayerName(Object *ob) +{ + Mesh *me = (Mesh*)ob->data; + + int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); + if (num_layers) + return std::string(bc_CustomData_get_active_layer_name(&me->fdata, CD_MTFACE)); + + return ""; +} + + +EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryEffects(sw){} +void EffectsExporter::exportEffects(Scene *sce) +{ + openLibrary(); + MaterialFunctor mf; + mf.forEachMaterialInScene<EffectsExporter>(sce, *this); + + closeLibrary(); +} + +void EffectsExporter::operator()(Material *ma, Object *ob) +{ + // create a list of indices to textures of type TEX_IMAGE + std::vector<int> tex_indices; + createTextureIndices(ma, tex_indices); + + openEffect(translate_id(id_name(ma)) + "-effect"); + + COLLADASW::EffectProfile ep(mSW); + ep.setProfileType(COLLADASW::EffectProfile::COMMON); + ep.openProfile(); + // set shader type - one of three blinn, phong or lambert + if (ma->spec_shader == MA_SPEC_BLINN) { + ep.setShaderType(COLLADASW::EffectProfile::BLINN); + // shininess + ep.setShininess(ma->har); + } + else if (ma->spec_shader == MA_SPEC_PHONG) { + ep.setShaderType(COLLADASW::EffectProfile::PHONG); + // shininess + ep.setShininess(ma->har); + } + else { + // XXX write warning "Current shader type is not supported" + ep.setShaderType(COLLADASW::EffectProfile::LAMBERT); + } + // index of refraction + if (ma->mode & MA_RAYTRANSP) { + ep.setIndexOfRefraction(ma->ang); + } + else { + ep.setIndexOfRefraction(1.0f); + } + + COLLADASW::ColorOrTexture cot; + + // transparency + if (ma->mode & MA_TRANSP) { + // Tod: because we are in A_ONE mode transparency is calculated like this: + ep.setTransparency(ma->alpha); + // cot = getcol(1.0f, 1.0f, 1.0f, 1.0f); + // ep.setTransparent(cot); + } + + // emission + cot=getcol(ma->emit, ma->emit, ma->emit, 1.0f); + ep.setEmission(cot); + + // diffuse multiplied by diffuse intensity + cot = getcol(ma->r * ma->ref, ma->g * ma->ref, ma->b * ma->ref, 1.0f); + ep.setDiffuse(cot); + + // ambient + cot = getcol(ma->ambr, ma->ambg, ma->ambb, 1.0f); + ep.setAmbient(cot); + + // reflective, reflectivity + if (ma->mode & MA_RAYMIRROR) { + cot = getcol(ma->mirr, ma->mirg, ma->mirb, 1.0f); + ep.setReflective(cot); + ep.setReflectivity(ma->ray_mirror); + } + // else { + // cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f); + // ep.setReflective(cot); + // ep.setReflectivity(ma->spec); + // } + + // specular + if (ep.getShaderType() != COLLADASW::EffectProfile::LAMBERT) { + cot = getcol(ma->specr * ma->spec, ma->specg * ma->spec, ma->specb * ma->spec, 1.0f); + ep.setSpecular(cot); + } + + // XXX make this more readable if possible + + // create <sampler> and <surface> for each image + COLLADASW::Sampler samplers[MAX_MTEX]; + //COLLADASW::Surface surfaces[MAX_MTEX]; + //void *samp_surf[MAX_MTEX][2]; + void *samp_surf[MAX_MTEX][1]; + + // image to index to samp_surf map + // samp_surf[index] stores 2 pointers, sampler and surface + std::map<std::string, int> im_samp_map; + + unsigned int a, b; + for (a = 0, b = 0; a < tex_indices.size(); a++) { + MTex *t = ma->mtex[tex_indices[a]]; + Image *ima = t->tex->ima; + + // Image not set for texture + if(!ima) continue; + + std::string key(id_name(ima)); + key = translate_id(key); + + // create only one <sampler>/<surface> pair for each unique image + if (im_samp_map.find(key) == im_samp_map.end()) { + // //<newparam> <surface> <init_from> + // COLLADASW::Surface surface(COLLADASW::Surface::SURFACE_TYPE_2D, + // key + COLLADASW::Surface::SURFACE_SID_SUFFIX); + // COLLADASW::SurfaceInitOption sio(COLLADASW::SurfaceInitOption::INIT_FROM); + // sio.setImageReference(key); + // surface.setInitOption(sio); + + // COLLADASW::NewParamSurface surface(mSW); + // surface->setParamType(COLLADASW::CSW_SURFACE_TYPE_2D); + + //<newparam> <sampler> <source> + COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D, + key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX, + key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); + sampler.setImageId(key); + // copy values to arrays since they will live longer + samplers[a] = sampler; + //surfaces[a] = surface; + + // store pointers so they can be used later when we create <texture>s + samp_surf[b][0] = &samplers[a]; + //samp_surf[b][1] = &surfaces[a]; + + im_samp_map[key] = b; + b++; + } + } + + // used as fallback when MTex->uvname is "" (this is pretty common) + // it is indeed the correct value to use in that case + std::string active_uv(getActiveUVLayerName(ob)); + + // write textures + // XXX very slow + for (a = 0; a < tex_indices.size(); a++) { + MTex *t = ma->mtex[tex_indices[a]]; + Image *ima = t->tex->ima; + + // Image not set for texture + if(!ima) continue; + + // we assume map input is always TEXCO_UV + + std::string key(id_name(ima)); + key = translate_id(key); + int i = im_samp_map[key]; + COLLADASW::Sampler *sampler = (COLLADASW::Sampler*)samp_surf[i][0]; + //COLLADASW::Surface *surface = (COLLADASW::Surface*)samp_surf[i][1]; + + std::string uvname = strlen(t->uvname) ? t->uvname : active_uv; + + // color + if (t->mapto & MAP_COL) { + ep.setDiffuse(createTexture(ima, uvname, sampler)); + } + // ambient + if (t->mapto & MAP_AMB) { + ep.setAmbient(createTexture(ima, uvname, sampler)); + } + // specular + if (t->mapto & MAP_SPEC) { + ep.setSpecular(createTexture(ima, uvname, sampler)); + } + // emission + if (t->mapto & MAP_EMIT) { + ep.setEmission(createTexture(ima, uvname, sampler)); + } + // reflective + if (t->mapto & MAP_REF) { + ep.setReflective(createTexture(ima, uvname, sampler)); + } + // alpha + if (t->mapto & MAP_ALPHA) { + ep.setTransparent(createTexture(ima, uvname, sampler)); + } + // extension: + // Normal map --> Must be stored with <extra> tag as different technique, + // since COLLADA doesn't support normal maps, even in current COLLADA 1.5. + if (t->mapto & MAP_NORM) { + COLLADASW::Texture texture(key); + texture.setTexcoord(uvname); + texture.setSampler(*sampler); + // technique FCOLLADA, with the <bump> tag, is most likely the best understood, + // most widespread de-facto standard. + texture.setProfileName("FCOLLADA"); + texture.setChildElementName("bump"); + ep.addExtraTechniqueColorOrTexture(COLLADASW::ColorOrTexture(texture)); + } + } + // performs the actual writing + ep.addProfileElements(); + bool twoSided = false; + if (ob->type == OB_MESH && ob->data) { + Mesh *me = (Mesh*)ob->data; + if (me->flag & ME_TWOSIDED) + twoSided = true; + } + if (twoSided) + ep.addExtraTechniqueParameter("GOOGLEEARTH", "show_double_sided", 1); + ep.addExtraTechniques(mSW); + + ep.closeProfile(); + if (twoSided) + mSW->appendTextBlock("<extra><technique profile=\"MAX3D\"><double_sided>1</double_sided></technique></extra>"); + closeEffect(); +} + +COLLADASW::ColorOrTexture EffectsExporter::createTexture(Image *ima, + std::string& uv_layer_name, + COLLADASW::Sampler *sampler + /*COLLADASW::Surface *surface*/) +{ + + COLLADASW::Texture texture(translate_id(id_name(ima))); + texture.setTexcoord(uv_layer_name); + //texture.setSurface(*surface); + texture.setSampler(*sampler); + + COLLADASW::ColorOrTexture cot(texture); + return cot; +} + +COLLADASW::ColorOrTexture EffectsExporter::getcol(float r, float g, float b, float a) +{ + COLLADASW::Color color(r,g,b,a); + COLLADASW::ColorOrTexture cot(color); + return cot; +} + +//returns the array of mtex indices which have image +//need this for exporting textures +void EffectsExporter::createTextureIndices(Material *ma, std::vector<int> &indices) +{ + indices.clear(); + + for (int a = 0; a < MAX_MTEX; a++) { + if (ma->mtex[a] && + ma->mtex[a]->tex && + ma->mtex[a]->tex->type == TEX_IMAGE && + ma->mtex[a]->texco == TEXCO_UV){ + indices.push_back(a); + } + } +} diff --git a/source/blender/collada/EffectExporter.h b/source/blender/collada/EffectExporter.h new file mode 100644 index 00000000000..3a3de95458b --- /dev/null +++ b/source/blender/collada/EffectExporter.h @@ -0,0 +1,66 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file EffectExporter.h + * \ingroup collada + */ + +#ifndef __EFFECTEXPORTER_H__ +#define __EFFECTEXPORTER_H__ + +#include <string> +#include <vector> + +#include "COLLADASWColorOrTexture.h" +#include "COLLADASWStreamWriter.h" +#include "COLLADASWSampler.h" +#include "COLLADASWLibraryEffects.h" + +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +class EffectsExporter: COLLADASW::LibraryEffects +{ +public: + EffectsExporter(COLLADASW::StreamWriter *sw); + void exportEffects(Scene *sce); + + void operator()(Material *ma, Object *ob); + + COLLADASW::ColorOrTexture createTexture(Image *ima, + std::string& uv_layer_name, + COLLADASW::Sampler *sampler + /*COLLADASW::Surface *surface*/); + + COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a); + + //returns the array of mtex indices which have image + //need this for exporting textures + void createTextureIndices(Material *ma, std::vector<int> &indices); +}; + +#endif diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp new file mode 100644 index 00000000000..a637bdc5155 --- /dev/null +++ b/source/blender/collada/GeometryExporter.cpp @@ -0,0 +1,477 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "COLLADASWPrimitves.h" +#include "COLLADASWSource.h" +#include "COLLADASWVertices.h" +#include "COLLADABUUtils.h" + +#include "GeometryExporter.h" + +#include "DNA_meshdata_types.h" +#include "BKE_customdata.h" +#include "BKE_material.h" + +#include "collada_internal.h" + +// TODO: optimize UV sets by making indexed list with duplicates removed +GeometryExporter::GeometryExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryGeometries(sw) {} + + +void GeometryExporter::exportGeom(Scene *sce) +{ + openLibrary(); + + mScene = sce; + GeometryFunctor gf; + gf.forEachMeshObjectInScene<GeometryExporter>(sce, *this); + + closeLibrary(); +} + +void GeometryExporter::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_id = get_geometry_id(ob); + std::vector<Normal> nor; + std::vector<Face> norind; + + // Skip if linked geometry was already exported from another reference + if (exportedGeometry.find(geom_id) != exportedGeometry.end()) + return; + exportedGeometry.insert(geom_id); + + bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); + + create_normals(nor, norind, me); + + // openMesh(geoId, geoName, meshId) + openMesh(geom_id); + + // writes <source> for vertex coords + createVertsSource(geom_id, me); + + // writes <source> for normal coords + createNormalsSource(geom_id, me, nor); + + bool has_uvs = (bool)CustomData_has_layer(&me->fdata, CD_MTFACE); + + // writes <source> for uv coords if mesh has uv coords + if (has_uvs) + createTexcoordsSource(geom_id, me); + + if (has_color) + createVertexColorSource(geom_id, me); + + // <vertices> + COLLADASW::Vertices verts(mSW); + verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX)); + COLLADASW::InputList &input_list = verts.getInputList(); + COLLADASW::Input input(COLLADASW::InputSemantic::POSITION, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); + input_list.push_back(input); + verts.add(); + + // XXX slow + if (ob->totcol) { + for(int a = 0; a < ob->totcol; a++) { + createPolylist(a, has_uvs, has_color, ob, geom_id, norind); + } + } + else { + createPolylist(0, has_uvs, has_color, ob, geom_id, norind); + } + + closeMesh(); + closeGeometry(); + +#if 0 + dm->release(dm); +#endif +} + +// powerful because it handles both cases when there is material and when there's not +void GeometryExporter::createPolylist(int material_index, + bool has_uvs, + bool has_color, + Object *ob, + std::string& geom_id, + std::vector<Face>& norind) +{ + Mesh *me = (Mesh*)ob->data; + MFace *mfaces = me->mface; + int totfaces = me->totface; + + // <vcount> + int i; + int faces_in_polylist = 0; + std::vector<unsigned long> vcount_list; + + // count faces with this material + for (i = 0; i < totfaces; i++) { + MFace *f = &mfaces[i]; + + if (f->mat_nr == material_index) { + faces_in_polylist++; + if (f->v4 == 0) { + vcount_list.push_back(3); + } + else { + vcount_list.push_back(4); + } + } + } + + // no faces using this material + if (faces_in_polylist == 0) { + fprintf(stderr, "%s: no faces use material %d\n", id_name(ob).c_str(), material_index); + return; + } + + Material *ma = ob->totcol ? give_current_material(ob, material_index + 1) : NULL; + COLLADASW::Polylist polylist(mSW); + + // sets count attribute in <polylist> + polylist.setCount(faces_in_polylist); + + // sets material name + if (ma) { + polylist.setMaterial(translate_id(id_name(ma))); + } + + COLLADASW::InputList &til = polylist.getInputList(); + + // creates <input> in <polylist> for vertices + COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), 0); + + // creates <input> in <polylist> for normals + COLLADASW::Input input2(COLLADASW::InputSemantic::NORMAL, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL), 1); + + til.push_back(input1); + til.push_back(input2); + + // if mesh has uv coords writes <input> for TEXCOORD + int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); + + for (i = 0; i < num_layers; i++) { + // char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i); + COLLADASW::Input input3(COLLADASW::InputSemantic::TEXCOORD, + makeUrl(makeTexcoordSourceId(geom_id, i)), + 2, // offset always 2, this is only until we have optimized UV sets + i // set number equals UV layer index + ); + til.push_back(input3); + } + + if (has_color) { + COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR, getUrlBySemantics(geom_id, COLLADASW::InputSemantic::COLOR), has_uvs ? 3 : 2); + til.push_back(input4); + } + + // sets <vcount> + polylist.setVCountList(vcount_list); + + // performs the actual writing + polylist.prepareToAppendValues(); + + // <p> + int texindex = 0; + for (i = 0; i < totfaces; i++) { + MFace *f = &mfaces[i]; + + if (f->mat_nr == material_index) { + + unsigned int *v = &f->v1; + unsigned int *n = &norind[i].v1; + for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { + polylist.appendValues(v[j]); + polylist.appendValues(n[j]); + + if (has_uvs) + polylist.appendValues(texindex + j); + + if (has_color) + polylist.appendValues(texindex + j); + } + } + + texindex += 3; + if (f->v4 != 0) + texindex++; + } + + polylist.finish(); +} + +// creates <source> for positions +void GeometryExporter::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_id, COLLADASW::InputSemantic::POSITION)); + source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) + + ARRAY_ID_SUFFIX); + source.setAccessorCount(totverts); + source.setAccessorStride(3); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + /*main function, it creates <source id = "">, <float_array id = "" + count = ""> */ + source.prepareToAppendValues(); + //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.finish(); + +} + +void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) +{ + if (!CustomData_has_layer(&me->fdata, CD_MCOL)) + return; + + MFace *f; + int totcolor = 0, i, j; + + for (i = 0, f = me->mface; i < me->totface; i++, f++) + totcolor += f->v4 ? 4 : 3; + + COLLADASW::FloatSourceF source(mSW); + source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::COLOR)); + source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::COLOR) + ARRAY_ID_SUFFIX); + source.setAccessorCount(totcolor); + source.setAccessorStride(3); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("R"); + param.push_back("G"); + param.push_back("B"); + + source.prepareToAppendValues(); + + int index = CustomData_get_active_layer_index(&me->fdata, CD_MCOL); + + MCol *mcol = (MCol*)me->fdata.layers[index].data; + MCol *c = mcol; + + for (i = 0, f = me->mface; i < me->totface; i++, c += 4, f++) + for (j = 0; j < (f->v4 ? 4 : 3); j++) + source.appendValues(c[j].b / 255.0f, c[j].g / 255.0f, c[j].r / 255.0f); + + source.finish(); +} + +std::string GeometryExporter::makeTexcoordSourceId(std::string& geom_id, int layer_index) +{ + char suffix[20]; + sprintf(suffix, "-%d", layer_index); + return getIdBySemantics(geom_id, COLLADASW::InputSemantic::TEXCOORD) + suffix; +} + +//creates <source> for texcoords +void GeometryExporter::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; + + // count totuv + for (i = 0; i < totfaces; i++) { + MFace *f = &mfaces[i]; + if (f->v4 == 0) { + totuv+=3; + } + else { + totuv+=4; + } + } + + int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE); + + // write <source> for each layer + // each <source> will get id like meshName + "map-channel-1" + for (int a = 0; a < num_layers; a++) { + MTFace *tface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a); + // char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a); + + COLLADASW::FloatSourceF source(mSW); + std::string layer_id = makeTexcoordSourceId(geom_id, a); + source.setId(layer_id); + source.setArrayId(layer_id + ARRAY_ID_SUFFIX); + + source.setAccessorCount(totuv); + source.setAccessorStride(2); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("S"); + param.push_back("T"); + + source.prepareToAppendValues(); + + for (i = 0; i < totfaces; i++) { + MFace *f = &mfaces[i]; + + for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) { + source.appendValues(tface[i].uv[j][0], + tface[i].uv[j][1]); + } + } + + source.finish(); + } +} + + +//creates <source> for normals +void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal>& nor) +{ +#if 0 + int totverts = dm->getNumVerts(dm); + MVert *verts = dm->getVertArray(dm); +#endif + + COLLADASW::FloatSourceF source(mSW); + source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL)); + source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL) + + ARRAY_ID_SUFFIX); + source.setAccessorCount((unsigned long)nor.size()); + source.setAccessorStride(3); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + + source.prepareToAppendValues(); + + std::vector<Normal>::iterator it; + for (it = nor.begin(); it != nor.end(); it++) { + Normal& n = *it; + source.appendValues(n.x, n.y, n.z); + } + + source.finish(); +} + +void GeometryExporter::create_normals(std::vector<Normal> &nor, std::vector<Face> &ind, Mesh *me) +{ + int i, j, v; + MVert *vert = me->mvert; + std::map<unsigned int, unsigned int> nshar; + + for (i = 0; i < me->totface; i++) { + MFace *fa = &me->mface[i]; + Face f; + unsigned int *nn = &f.v1; + unsigned int *vv = &fa->v1; + + memset(&f, 0, sizeof(f)); + v = fa->v4 == 0 ? 3 : 4; + + if (!(fa->flag & ME_SMOOTH)) { + Normal n; + if (v == 4) + normal_quad_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co, vert[fa->v4].co); + else + normal_tri_v3(&n.x, vert[fa->v1].co, vert[fa->v2].co, vert[fa->v3].co); + nor.push_back(n); + } + + for (j = 0; j < v; j++) { + if (fa->flag & ME_SMOOTH) { + if (nshar.find(*vv) != nshar.end()) + *nn = nshar[*vv]; + else { + Normal n = { + vert[*vv].no[0]/32767.0, + vert[*vv].no[1]/32767.0, + vert[*vv].no[2]/32767.0 + }; + nor.push_back(n); + *nn = (unsigned int)nor.size() - 1; + nshar[*vv] = *nn; + } + vv++; + } + else { + *nn = (unsigned int)nor.size() - 1; + } + nn++; + } + + ind.push_back(f); + } +} + +std::string GeometryExporter::getIdBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix) { + return geom_id + getSuffixBySemantic(type) + other_suffix; +} + + +COLLADASW::URI GeometryExporter::getUrlBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix) { + + std::string id(getIdBySemantics(geom_id, type, other_suffix)); + return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); + +} + +COLLADASW::URI GeometryExporter::makeUrl(std::string id) +{ + return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); +} + + +/* int GeometryExporter::getTriCount(MFace *faces, int totface) { + int i; + int tris = 0; + for (i = 0; i < totface; i++) { + // if quad + if (faces[i].v4 != 0) + tris += 2; + else + tris++; + } + + return tris; + }*/ diff --git a/source/blender/collada/GeometryExporter.h b/source/blender/collada/GeometryExporter.h new file mode 100644 index 00000000000..0b9abaebc25 --- /dev/null +++ b/source/blender/collada/GeometryExporter.h @@ -0,0 +1,121 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file GeometryExporter.h + * \ingroup collada + */ + +#ifndef __GEOMETRYEXPORTER_H__ +#define __GEOMETRYEXPORTER_H__ + +#include <string> +#include <vector> +#include <set> + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryGeometries.h" +#include "COLLADASWInputList.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +// TODO: optimize UV sets by making indexed list with duplicates removed +class GeometryExporter : COLLADASW::LibraryGeometries +{ + struct Face + { + unsigned int v1, v2, v3, v4; + }; + + struct Normal + { + float x, y, z; + }; + + Scene *mScene; + +public: + GeometryExporter(COLLADASW::StreamWriter *sw); + + void exportGeom(Scene *sce); + + void operator()(Object *ob); + + // powerful because it handles both cases when there is material and when there's not + void createPolylist(int material_index, + bool has_uvs, + bool has_color, + Object *ob, + std::string& geom_id, + std::vector<Face>& norind); + + // creates <source> for positions + void createVertsSource(std::string geom_id, Mesh *me); + + void createVertexColorSource(std::string geom_id, Mesh *me); + + std::string makeTexcoordSourceId(std::string& geom_id, int layer_index); + + //creates <source> for texcoords + void createTexcoordsSource(std::string geom_id, Mesh *me); + + //creates <source> for normals + void createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal>& nor); + + void create_normals(std::vector<Normal> &nor, std::vector<Face> &ind, Mesh *me); + + std::string getIdBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix = ""); + + COLLADASW::URI getUrlBySemantics(std::string geom_id, COLLADASW::InputSemantic::Semantics type, std::string other_suffix = ""); + + COLLADASW::URI makeUrl(std::string id); + + /* int getTriCount(MFace *faces, int totface);*/ +private: + std::set<std::string> exportedGeometry; +}; + +struct GeometryFunctor { + // f should have + // void operator()(Object* ob) + template<class Functor> + void forEachMeshObjectInScene(Scene *sce, Functor &f) + { + + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + if (ob->type == OB_MESH && ob->data) { + f(ob); + } + base= base->next; + + } + } +}; + +#endif diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp new file mode 100644 index 00000000000..e87f6208d59 --- /dev/null +++ b/source/blender/collada/ImageExporter.cpp @@ -0,0 +1,94 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "COLLADABUURI.h" +#include "COLLADASWImage.h" + +#include "ImageExporter.h" +#include "MaterialExporter.h" + +#include "DNA_texture_types.h" + +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_utildefines.h" +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +ImagesExporter::ImagesExporter(COLLADASW::StreamWriter *sw, const char* filename) : COLLADASW::LibraryImages(sw), mfilename(filename) +{} + +void ImagesExporter::exportImages(Scene *sce) +{ + openLibrary(); + MaterialFunctor mf; + mf.forEachMaterialInScene<ImagesExporter>(sce, *this); + + closeLibrary(); +} + +void ImagesExporter::operator()(Material *ma, Object *ob) +{ + int a; + for (a = 0; a < MAX_MTEX; a++) { + MTex *mtex = ma->mtex[a]; + if (mtex && mtex->tex && mtex->tex->ima) { + + Image *image = mtex->tex->ima; + std::string name(id_name(image)); + name = translate_id(name); + char rel[FILE_MAX]; + char abs[FILE_MAX]; + char src[FILE_MAX]; + char dir[FILE_MAX]; + + BLI_split_dirfile(mfilename, dir, NULL); + + BKE_rebase_path(abs, sizeof(abs), rel, sizeof(rel), G.main->name, image->name, dir); + + if (abs[0] != '\0') { + + // make absolute source path + BLI_strncpy(src, image->name, sizeof(src)); + BLI_path_abs(src, G.main->name); + + // make dest directory if it doesn't exist + BLI_make_existing_file(abs); + + if (BLI_copy_fileops(src, abs) != 0) { + fprintf(stderr, "Cannot copy image to file's directory. \n"); + } + } + + if (find(mImages.begin(), mImages.end(), name) == mImages.end()) { + COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(rel)), name, name); /* set name also to mNameNC. This helps other viewers import files exported from Blender better */ + img.add(mSW); + + mImages.push_back(name); + } + } + } +} diff --git a/source/blender/collada/ImageExporter.h b/source/blender/collada/ImageExporter.h new file mode 100644 index 00000000000..901e6be3ecb --- /dev/null +++ b/source/blender/collada/ImageExporter.h @@ -0,0 +1,54 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ImageExporter.h + * \ingroup collada + */ + +#ifndef __IMAGEEXPORTER_H__ +#define __IMAGEEXPORTER_H__ + +#include <vector> +#include <string> + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryImages.h" + +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +class ImagesExporter: COLLADASW::LibraryImages +{ + const char *mfilename; + std::vector<std::string> mImages; // contains list of written images, to avoid duplicates +public: + ImagesExporter(COLLADASW::StreamWriter *sw, const char* filename); + + void exportImages(Scene *sce); + void operator()(Material *ma, Object *ob); +}; + +#endif diff --git a/source/blender/collada/InstanceWriter.cpp b/source/blender/collada/InstanceWriter.cpp new file mode 100644 index 00000000000..a135b51f025 --- /dev/null +++ b/source/blender/collada/InstanceWriter.cpp @@ -0,0 +1,64 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string> + +#include "COLLADASWInstanceMaterial.h" + +#include "BKE_customdata.h" +#include "BKE_material.h" + +#include "DNA_mesh_types.h" + +#include "InstanceWriter.h" + +#include "collada_internal.h" +#include "collada_utils.h" + +void InstanceWriter::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)); + matid = translate_id(matid); + 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 = bc_CustomData_get_layer_name(&me->fdata, CD_MTFACE, b); + im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", b)); + } + + iml.push_back(im); + } + } +} diff --git a/source/blender/collada/InstanceWriter.h b/source/blender/collada/InstanceWriter.h new file mode 100644 index 00000000000..03be8d2426c --- /dev/null +++ b/source/blender/collada/InstanceWriter.h @@ -0,0 +1,43 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file InstanceWriter.h + * \ingroup collada + */ + +#ifndef __INSTANCEWRITER_H__ +#define __INSTANCEWRITER_H__ + +#include "COLLADASWBindMaterial.h" + +#include "DNA_object_types.h" + +class InstanceWriter +{ +protected: + void add_material_bindings(COLLADASW::BindMaterial& bind_material, Object *ob); +}; + +#endif diff --git a/source/blender/collada/LightExporter.cpp b/source/blender/collada/LightExporter.cpp new file mode 100644 index 00000000000..d13a053b8fe --- /dev/null +++ b/source/blender/collada/LightExporter.cpp @@ -0,0 +1,139 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string> + +#include "COLLADASWColor.h" +#include "COLLADASWLight.h" + +#include "DNA_lamp_types.h" + +#include "BLI_math.h" + +#include "LightExporter.h" +#include "collada_internal.h" + +template<class Functor> +void forEachLampObjectInScene(Scene *sce, Functor &f) +{ + Base *base= (Base*) sce->base.first; + while(base) { + Object *ob = base->object; + + if (ob->type == OB_LAMP && ob->data) { + f(ob); + } + base= base->next; + } +} + +LightsExporter::LightsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryLights(sw){} + +void LightsExporter::exportLights(Scene *sce) +{ + openLibrary(); + + forEachLampObjectInScene(sce, *this); + + closeLibrary(); +} +void LightsExporter::operator()(Object *ob) +{ + Lamp *la = (Lamp*)ob->data; + std::string la_id(get_light_id(ob)); + std::string la_name(id_name(la)); + COLLADASW::Color col(la->r, la->g, la->b); + float att1, att2; + float e, d, constatt, linatt, quadatt; + att1 = att2 = 0.0f; + + if(la->falloff_type==LA_FALLOFF_INVLINEAR) { + att1 = 1.0f; + att2 = 0.0f; + } + else if(la->falloff_type==LA_FALLOFF_INVSQUARE) { + att1 = 0.0f; + att2 = 1.0f; + } + else if(la->falloff_type==LA_FALLOFF_SLIDERS) { + att1 = la->att1; + att2 = la->att2; + } + + e = la->energy; + d = la->dist; + + constatt = linatt = quadatt = MAXFLOAT; + if(e > 0.0f) { + constatt = 1.0f/e; + linatt = att1/(d*e); + quadatt = att2/(d*d*(e*2)); + } + + // sun + if (la->type == LA_SUN) { + COLLADASW::DirectionalLight cla(mSW, la_id, la_name, e); + cla.setColor(col); + cla.setConstantAttenuation(constatt); + addLight(cla); + } + // hemi + else if (la->type == LA_HEMI) { + COLLADASW::AmbientLight cla(mSW, la_id, la_name, e); + cla.setColor(col); + cla.setConstantAttenuation(constatt); + addLight(cla); + } + // spot + else if (la->type == LA_SPOT) { + COLLADASW::SpotLight cla(mSW, la_id, la_name, e); + cla.setColor(col); + cla.setFallOffAngle(la->spotsize); + cla.setFallOffExponent(la->spotblend); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + addLight(cla); + } + // lamp + else if (la->type == LA_LOCAL) { + COLLADASW::PointLight cla(mSW, la_id, la_name, e); + cla.setColor(col); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + addLight(cla); + } + // area lamp is not supported + // it will be exported as a local lamp + else { + COLLADASW::PointLight cla(mSW, la_id, la_name, e); + cla.setColor(col); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + addLight(cla); + } +} diff --git a/source/blender/collada/LightExporter.h b/source/blender/collada/LightExporter.h new file mode 100644 index 00000000000..685a50ff17e --- /dev/null +++ b/source/blender/collada/LightExporter.h @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file LightExporter.h + * \ingroup collada + */ + +#ifndef __LIGHTEXPORTER_H__ +#define __LIGHTEXPORTER_H__ + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryLights.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +class LightsExporter: COLLADASW::LibraryLights +{ +public: + LightsExporter(COLLADASW::StreamWriter *sw); + void exportLights(Scene *sce); + void operator()(Object *ob); +}; + +#endif diff --git a/source/blender/collada/MaterialExporter.cpp b/source/blender/collada/MaterialExporter.cpp new file mode 100644 index 00000000000..a0d66e6467d --- /dev/null +++ b/source/blender/collada/MaterialExporter.cpp @@ -0,0 +1,53 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + + +#include "MaterialExporter.h" +#include "COLLADABUUtils.h" +#include "collada_internal.h" + +MaterialsExporter::MaterialsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryMaterials(sw){} + +void MaterialsExporter::exportMaterials(Scene *sce) +{ + openLibrary(); + + MaterialFunctor mf; + mf.forEachMaterialInScene<MaterialsExporter>(sce, *this); + + closeLibrary(); +} + +void MaterialsExporter::operator()(Material *ma, Object *ob) +{ + std::string name(id_name(ma)); + + openMaterial(translate_id(name), name); + + std::string efid = translate_id(name) + "-effect"; + addInstanceEffect(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, efid)); + + closeMaterial(); +} diff --git a/source/blender/collada/MaterialExporter.h b/source/blender/collada/MaterialExporter.h new file mode 100644 index 00000000000..033c8526346 --- /dev/null +++ b/source/blender/collada/MaterialExporter.h @@ -0,0 +1,98 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file MaterialExporter.h + * \ingroup collada + */ + +#ifndef __MATERIALEXPORTER_H__ +#define __MATERIALEXPORTER_H__ + +#include <string> +#include <vector> + +#include "COLLADASWLibraryMaterials.h" +#include "COLLADASWStreamWriter.h" + +#include "BKE_material.h" + +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "GeometryExporter.h" +#include "collada_internal.h" + +class MaterialsExporter: COLLADASW::LibraryMaterials +{ +public: + MaterialsExporter(COLLADASW::StreamWriter *sw); + void exportMaterials(Scene *sce); + void operator()(Material *ma, Object *ob); +}; + +// used in forEachMaterialInScene +template <class Functor> +class ForEachMaterialFunctor +{ + std::vector<std::string> mMat; // contains list of material names, to avoid duplicate calling of f + Functor *f; +public: + ForEachMaterialFunctor(Functor*f) : f(f) {} + + void operator ()(Object *ob) + { + int a; + for(a = 0; a < ob->totcol; a++) { + + Material *ma = give_current_material(ob, a+1); + + if (!ma) continue; + + std::string translated_id = translate_id(id_name(ma)); + if (find(mMat.begin(), mMat.end(), translated_id) == mMat.end()) { + (*this->f)(ma, ob); + + mMat.push_back(translated_id); + } + } + } +}; + +struct MaterialFunctor { + // calls f for each unique material linked to each object in sce + // f should have + // void operator()(Material* ma) + template<class Functor> + void forEachMaterialInScene(Scene *sce, Functor &f) + { + ForEachMaterialFunctor<Functor> matfunc(&f); + GeometryFunctor gf; + gf.forEachMeshObjectInScene<ForEachMaterialFunctor<Functor> >(sce, matfunc); + } +}; + + +#endif diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp new file mode 100644 index 00000000000..d458b47885f --- /dev/null +++ b/source/blender/collada/MeshImporter.cpp @@ -0,0 +1,915 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <algorithm> + +#if !defined(WIN32) || defined(FREE_WINDOWS) +#include <iostream> +#endif + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWMeshVertexData.h" +#include "COLLADAFWPolygons.h" + +extern "C" { +#include "BKE_blender.h" +#include "BKE_customdata.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "MEM_guardedalloc.h" +} + +#include "ArmatureImporter.h" +#include "MeshImporter.h" +#include "collada_utils.h" + +// works for COLLADAFW::Node, COLLADAFW::Geometry +template<class T> +static const char *bc_get_dae_name(T *node) +{ + const std::string& name = node->getName(); + return name.size() ? name.c_str() : node->getOriginalId().c_str(); +} + +static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type) +{ + switch (type) { + case COLLADAFW::MeshPrimitive::LINES: + return "LINES"; + case COLLADAFW::MeshPrimitive::LINE_STRIPS: + return "LINESTRIPS"; + case COLLADAFW::MeshPrimitive::POLYGONS: + return "POLYGONS"; + case COLLADAFW::MeshPrimitive::POLYLIST: + return "POLYLIST"; + case COLLADAFW::MeshPrimitive::TRIANGLES: + return "TRIANGLES"; + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: + return "TRIANGLE_FANS"; + case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS: + return "TRIANGLE_FANS"; + case COLLADAFW::MeshPrimitive::POINTS: + return "POINTS"; + case COLLADAFW::MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE: + return "UNDEFINED_PRIMITIVE_TYPE"; + } + return "UNKNOWN"; +} + +static const char *bc_geomTypeToStr(COLLADAFW::Geometry::GeometryType type) +{ + switch (type) { + case COLLADAFW::Geometry::GEO_TYPE_MESH: + return "MESH"; + case COLLADAFW::Geometry::GEO_TYPE_SPLINE: + return "SPLINE"; + case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH: + return "CONVEX_MESH"; + case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + + +UVDataWrapper::UVDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata) +{} + +#ifdef COLLADA_DEBUG +void WVDataWrapper::print() +{ + fprintf(stderr, "UVs:\n"); + switch(mVData->getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: + { + COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues(); + if (values->getCount()) { + for (int i = 0; i < values->getCount(); i += 2) { + fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i+1]); + } + } + } + break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: + { + COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues(); + if (values->getCount()) { + for (int i = 0; i < values->getCount(); i += 2) { + fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i+1]); + } + } + } + break; + } + fprintf(stderr, "\n"); +} +#endif + +void UVDataWrapper::getUV(int uv_index[2], float *uv) +{ + switch(mVData->getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: + { + COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues(); + if (values->empty()) return; + uv[0] = (*values)[uv_index[0]]; + uv[1] = (*values)[uv_index[1]]; + + } + break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: + { + COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues(); + if (values->empty()) return; + uv[0] = (float)(*values)[uv_index[0]]; + uv[1] = (float)(*values)[uv_index[1]]; + + } + break; + case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: + default: + fprintf(stderr, "MeshImporter.getUV(): unknown data type\n"); + } +} + +void MeshImporter::set_face_indices(MFace *mface, unsigned int *indices, bool quad) +{ + mface->v1 = indices[0]; + mface->v2 = indices[1]; + mface->v3 = indices[2]; + if (quad) mface->v4 = indices[3]; + else mface->v4 = 0; +#ifdef COLLADA_DEBUG + // fprintf(stderr, "%u, %u, %u \n", indices[0], indices[1], indices[2]); +#endif +} + +// not used anymore, test_index_face from blenkernel is better +#if 0 +// change face indices order so that v4 is not 0 +void MeshImporter::rotate_face_indices(MFace *mface) { + mface->v4 = mface->v1; + mface->v1 = mface->v2; + mface->v2 = mface->v3; + mface->v3 = 0; +} +#endif + +void MeshImporter::set_face_uv(MTFace *mtface, UVDataWrapper &uvs, + COLLADAFW::IndexList& index_list, unsigned int *tris_indices) +{ + int uv_indices[4][2]; + + // per face vertex indices, this means for quad we have 4 indices, not 8 + COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); + + // make indices into FloatOrDoubleArray + for (int i = 0; i < 3; i++) { + int uv_index = indices[tris_indices[i]]; + uv_indices[i][0] = uv_index * 2; + uv_indices[i][1] = uv_index * 2 + 1; + } + + uvs.getUV(uv_indices[0], mtface->uv[0]); + uvs.getUV(uv_indices[1], mtface->uv[1]); + uvs.getUV(uv_indices[2], mtface->uv[2]); +} + +void MeshImporter::set_face_uv(MTFace *mtface, UVDataWrapper &uvs, + COLLADAFW::IndexList& index_list, int index, bool quad) +{ + int uv_indices[4][2]; + + // per face vertex indices, this means for quad we have 4 indices, not 8 + COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); + + // make indices into FloatOrDoubleArray + for (int i = 0; i < (quad ? 4 : 3); i++) { + int uv_index = indices[index + i]; + uv_indices[i][0] = uv_index * 2; + uv_indices[i][1] = uv_index * 2 + 1; + } + + uvs.getUV(uv_indices[0], mtface->uv[0]); + uvs.getUV(uv_indices[1], mtface->uv[1]); + uvs.getUV(uv_indices[2], mtface->uv[2]); + + if (quad) uvs.getUV(uv_indices[3], mtface->uv[3]); + +#ifdef COLLADA_DEBUG + /*if (quad) { + fprintf(stderr, "face uv:\n" + "((%d, %d), (%d, %d), (%d, %d), (%d, %d))\n" + "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", + + uv_indices[0][0], uv_indices[0][1], + uv_indices[1][0], uv_indices[1][1], + uv_indices[2][0], uv_indices[2][1], + uv_indices[3][0], uv_indices[3][1], + + mtface->uv[0][0], mtface->uv[0][1], + mtface->uv[1][0], mtface->uv[1][1], + mtface->uv[2][0], mtface->uv[2][1], + mtface->uv[3][0], mtface->uv[3][1]); + } + else { + fprintf(stderr, "face uv:\n" + "((%d, %d), (%d, %d), (%d, %d))\n" + "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n", + + uv_indices[0][0], uv_indices[0][1], + uv_indices[1][0], uv_indices[1][1], + uv_indices[2][0], uv_indices[2][1], + + mtface->uv[0][0], mtface->uv[0][1], + mtface->uv[1][0], mtface->uv[1][1], + mtface->uv[2][0], mtface->uv[2][1]); + }*/ +#endif +} + +#ifdef COLLADA_DEBUG +void MeshImporter::print_index_list(COLLADAFW::IndexList& index_list) +{ + fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str()); + for (int i = 0; i < index_list.getIndicesCount(); i += 2) { + fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1)); + } + fprintf(stderr, "\n"); +} +#endif + +bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) +{ + COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); + + const char *name = bc_get_dae_name(mesh); + + for (unsigned i = 0; i < prim_arr.getCount(); i++) { + + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType(); + + const char *type_str = bc_primTypeToStr(type); + + // OpenCollada passes POLYGONS type for <polylist> + if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { + + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; + COLLADAFW::Polygons::VertexCountArray& vca = mpvc->getGroupedVerticesVertexCountArray(); + + for(unsigned int j = 0; j < vca.getCount(); j++){ + int count = vca[j]; + if (count < 3) { + fprintf(stderr, "Primitive %s in %s has at least one face with vertex count < 3\n", + type_str, name); + return false; + } + } + + } + else if(type != COLLADAFW::MeshPrimitive::TRIANGLES) { + fprintf(stderr, "Primitive type %s is not supported.\n", type_str); + return false; + } + } + + if (mesh->getPositions().empty()) { + fprintf(stderr, "Mesh %s has no vertices.\n", name); + return false; + } + + return true; +} + +void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) +{ + // vertices + COLLADAFW::MeshVertexData& pos = mesh->getPositions(); + int stride = pos.getStride(0); + if(stride==0) stride = 3; + + me->totvert = mesh->getPositions().getFloatValues()->getCount() / stride; + me->mvert = (MVert*)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); + + MVert *mvert; + int i; + + for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { + get_vector(mvert->co, pos, i, stride); + } +} + +int MeshImporter::triangulate_poly(unsigned int *indices, int totvert, MVert *verts, std::vector<unsigned int>& tri) +{ + ListBase dispbase; + DispList *dl; + float *vert; + int i = 0; + + dispbase.first = dispbase.last = NULL; + + dl = (DispList*)MEM_callocN(sizeof(DispList), "poly disp"); + dl->nr = totvert; + dl->type = DL_POLY; + dl->parts = 1; + dl->verts = vert = (float*)MEM_callocN(totvert * 3 * sizeof(float), "poly verts"); + dl->index = (int*)MEM_callocN(sizeof(int) * 3 * totvert, "dl index"); + + BLI_addtail(&dispbase, dl); + + for (i = 0; i < totvert; i++) { + copy_v3_v3(vert, verts[indices[i]].co); + vert += 3; + } + + filldisplist(&dispbase, &dispbase, 0); + + int tottri = 0; + dl= (DispList*)dispbase.first; + + if (dl->type == DL_INDEX3) { + tottri = dl->parts; + + int *index = dl->index; + for (i= 0; i < tottri; i++) { + int t[3]= {*index, *(index + 1), *(index + 2)}; + + std::sort(t, t + 3); + + tri.push_back(t[0]); + tri.push_back(t[1]); + tri.push_back(t[2]); + + index += 3; + } + } + + freedisplist(&dispbase); + + return tottri; +} + +int MeshImporter::count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me) +{ + COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); + unsigned int i; + int tottri = 0; + + for (i = 0; i < prim_arr.getCount(); i++) { + + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + int type = mp->getPrimitiveType(); + size_t prim_totface = mp->getFaceCount(); + unsigned int *indices = mp->getPositionIndices().getData(); + + if (type == COLLADAFW::MeshPrimitive::POLYLIST || + type == COLLADAFW::MeshPrimitive::POLYGONS) { + + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; + COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); + + for (unsigned int j = 0; j < prim_totface; j++) { + int vcount = vcounta[j]; + + if (vcount > 4) { + std::vector<unsigned int> tri; + + // tottri += triangulate_poly(indices, vcount, me->mvert, tri) - 1; // XXX why - 1?! + tottri += triangulate_poly(indices, vcount, me->mvert, tri); + } + + indices += vcount; + } + } + } + return tottri; +} + +// TODO: import uv set names +void MeshImporter::read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris) +{ + unsigned int i; + + // allocate faces + me->totface = mesh->getFacesCount() + new_tris; + me->mface = (MFace*)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface); + + // allocate UV layers + unsigned int totuvset = mesh->getUVCoords().getInputInfosArray().getCount(); + + for (i = 0; i < totuvset; i++) { + if (mesh->getUVCoords().getLength(i) == 0) { + totuvset = 0; + break; + } + } + + for (i = 0; i < totuvset; i++) { + COLLADAFW::MeshVertexData::InputInfos *info = mesh->getUVCoords().getInputInfosArray()[i]; + CustomData_add_layer_named(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface, info->mName.c_str()); + //this->set_layername_map[i] = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i); + } + + // activate the first uv layer + if (totuvset) me->mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, 0); + + UVDataWrapper uvs(mesh->getUVCoords()); + +#ifdef COLLADA_DEBUG + // uvs.print(); +#endif + + MFace *mface = me->mface; + + MaterialIdPrimitiveArrayMap mat_prim_map; + + int face_index = 0; + + COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); + + bool has_normals = mesh->hasNormals(); + COLLADAFW::MeshVertexData& nor = mesh->getNormals(); + + for (i = 0; i < prim_arr.getCount(); i++) { + + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + + // faces + size_t prim_totface = mp->getFaceCount(); + unsigned int *indices = mp->getPositionIndices().getData(); + unsigned int *nind = mp->getNormalIndices().getData(); + unsigned int j, k; + int type = mp->getPrimitiveType(); + int index = 0; + + // since we cannot set mface->mat_nr here, we store a portion of me->mface in Primitive + Primitive prim = {mface, 0}; + COLLADAFW::IndexListArray& index_list_array = mp->getUVCoordIndicesArray(); + +#ifdef COLLADA_DEBUG + /* + fprintf(stderr, "Primitive %d:\n", i); + for (int j = 0; j < totuvset; j++) { + print_index_list(*index_list_array[j]); + } + */ +#endif + + if (type == COLLADAFW::MeshPrimitive::TRIANGLES) { + for (j = 0; j < prim_totface; j++){ + + set_face_indices(mface, indices, false); + indices += 3; + +#if 0 + for (k = 0; k < totuvset; k++) { + if (!index_list_array.empty() && index_list_array[k]) { + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); + set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, false); + } + } +#else + for (k = 0; k < index_list_array.getCount(); k++) { + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); + set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, false); + } +#endif + + test_index_face(mface, &me->fdata, face_index, 3); + + if (has_normals) { + if (!flat_face(nind, nor, 3)) + mface->flag |= ME_SMOOTH; + + nind += 3; + } + + index += 3; + mface++; + face_index++; + prim.totface++; + } + } + else if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp; + COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray(); + + for (j = 0; j < prim_totface; j++) { + + // face + int vcount = vcounta[j]; + if (vcount == 3 || vcount == 4) { + + set_face_indices(mface, indices, vcount == 4); + + // set mtface for each uv set + // it is assumed that all primitives have equal number of UV sets + +#if 0 + for (k = 0; k < totuvset; k++) { + if (!index_list_array.empty() && index_list_array[k]) { + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); + set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, mface->v4 != 0); + } + } +#else + for (k = 0; k < index_list_array.getCount(); k++) { + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k); + set_face_uv(&mtface[face_index], uvs, *index_list_array[k], index, mface->v4 != 0); + } +#endif + + test_index_face(mface, &me->fdata, face_index, vcount); + + if (has_normals) { + if (!flat_face(nind, nor, vcount)) + mface->flag |= ME_SMOOTH; + + nind += vcount; + } + + mface++; + face_index++; + prim.totface++; + + } + else { + std::vector<unsigned int> tri; + + triangulate_poly(indices, vcount, me->mvert, tri); + + for (k = 0; k < tri.size() / 3; k++) { + int v = k * 3; + unsigned int uv_indices[3] = { + index + tri[v], + index + tri[v + 1], + index + tri[v + 2] + }; + unsigned int tri_indices[3] = { + indices[tri[v]], + indices[tri[v + 1]], + indices[tri[v + 2]] + }; + + set_face_indices(mface, tri_indices, false); + +#if 0 + for (unsigned int l = 0; l < totuvset; l++) { + if (!index_list_array.empty() && index_list_array[l]) { + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, l); + set_face_uv(&mtface[face_index], uvs, l, *index_list_array[l], uv_indices); + } + } +#else + for (unsigned int l = 0; l < index_list_array.getCount(); l++) { + int uvset_index = index_list_array[l]->getSetIndex(); + + // get mtface by face index and uv set index + MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, uvset_index); + set_face_uv(&mtface[face_index], uvs, *index_list_array[l], uv_indices); + } +#endif + + + test_index_face(mface, &me->fdata, face_index, 3); + + if (has_normals) { + unsigned int ntri[3] = {nind[tri[v]], nind[tri[v + 1]], nind[tri[v + 2]]}; + + if (!flat_face(ntri, nor, 3)) + mface->flag |= ME_SMOOTH; + } + + mface++; + face_index++; + prim.totface++; + } + + if (has_normals) + nind += vcount; + } + + index += vcount; + indices += vcount; + } + } + + mat_prim_map[mp->getMaterialId()].push_back(prim); + } + + geom_uid_mat_mapping_map[mesh->getUniqueId()] = mat_prim_map; +} + +void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride) +{ + i *= stride; + + switch(arr.getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: + { + COLLADAFW::ArrayPrimitiveType<float>* values = arr.getFloatValues(); + if (values->empty()) return; + + v[0] = (*values)[i++]; + v[1] = (*values)[i++]; + v[2] = (*values)[i]; + + } + break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: + { + COLLADAFW::ArrayPrimitiveType<double>* values = arr.getDoubleValues(); + if (values->empty()) return; + + v[0] = (float)(*values)[i++]; + v[1] = (float)(*values)[i++]; + v[2] = (float)(*values)[i]; + } + break; + default: + break; + } +} + +bool MeshImporter::flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count) +{ + float a[3], b[3]; + + get_vector(a, nor, *nind, 3); + normalize_v3(a); + + nind++; + + for (int i = 1; i < count; i++, nind++) { + get_vector(b, nor, *nind, 3); + normalize_v3(b); + + float dp = dot_v3v3(a, b); + + if (dp < 0.99999f || dp > 1.00001f) + return false; + } + + return true; +} + +MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) {} + +Object *MeshImporter::get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) +{ + if (uid_object_map.find(geom_uid) != uid_object_map.end()) + return uid_object_map[geom_uid]; + return NULL; +} + +MTex *MeshImporter::assign_textures_to_uvlayer(COLLADAFW::TextureCoordinateBinding &ctexture, + Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map, + MTex *color_texture) +{ + const COLLADAFW::TextureMapId texture_index = ctexture.getTextureMapId(); + const size_t setindex = ctexture.getSetIndex(); + std::string uvname = ctexture.getSemantic(); + + const CustomData *data = &me->fdata; + int layer_index = CustomData_get_layer_index(data, CD_MTFACE); + CustomDataLayer *cdl = &data->layers[layer_index+setindex]; + + /* set uvname to bind_vertex_input semantic */ + BLI_strncpy(cdl->name, uvname.c_str(), sizeof(cdl->name)); + + if (texindex_texarray_map.find(texture_index) == texindex_texarray_map.end()) { + + fprintf(stderr, "Cannot find texture array by texture index.\n"); + return color_texture; + } + + std::vector<MTex*> textures = texindex_texarray_map[texture_index]; + + std::vector<MTex*>::iterator it; + + for (it = textures.begin(); it != textures.end(); it++) { + + MTex *texture = *it; + + if (texture) { + BLI_strncpy(texture->uvname, uvname.c_str(), sizeof(texture->uvname)); + if (texture->mapto == MAP_COL) color_texture = texture; + } + } + return color_texture; +} + +MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, + std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, + Object *ob, const COLLADAFW::UniqueId *geom_uid, + MTex **color_texture, char *layername, MTFace *texture_face, + std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map, int mat_index) +{ + Mesh *me = (Mesh*)ob->data; + const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial(); + + // do we know this material? + if (uid_material_map.find(ma_uid) == uid_material_map.end()) { + + fprintf(stderr, "Cannot find material by UID.\n"); + return NULL; + } + + Material *ma = uid_material_map[ma_uid]; + assign_material(ob, ma, ob->totcol + 1); + + COLLADAFW::TextureCoordinateBindingArray& tex_array = + cmaterial.getTextureCoordinateBindingArray(); + TexIndexTextureArrayMap texindex_texarray_map = material_texture_mapping_map[ma]; + unsigned int i; + // loop through <bind_vertex_inputs> + for (i = 0; i < tex_array.getCount(); i++) { + + *color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map, + *color_texture); + } + + // set texture face + if (*color_texture && + strlen((*color_texture)->uvname) && + strcmp(layername, (*color_texture)->uvname) != 0) { + + texture_face = (MTFace*)CustomData_get_layer_named(&me->fdata, CD_MTFACE, + (*color_texture)->uvname); + strcpy(layername, (*color_texture)->uvname); + } + + MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; + COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId(); + + // assign material indices to mesh faces + if (mat_prim_map.find(mat_id) != mat_prim_map.end()) { + + std::vector<Primitive>& prims = mat_prim_map[mat_id]; + + std::vector<Primitive>::iterator it; + + for (it = prims.begin(); it != prims.end(); it++) { + Primitive& prim = *it; + i = 0; + while (i++ < prim.totface) { + prim.mface->mat_nr = mat_index; + prim.mface++; + // bind texture images to faces + if (texture_face && (*color_texture)) { + texture_face->mode = TF_TEX; + texture_face->tpage = (Image*)(*color_texture)->tex->ima; + texture_face++; + } + } + } + } + + return texture_face; +} + + +Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom, + bool isController, + std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, + std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map) +{ + const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId(); + + // check if node instanciates controller or geometry + if (isController) { + + geom_uid = armature_importer->get_geometry_uid(*geom_uid); + + if (!geom_uid) { + fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n"); + return NULL; + } + } + else { + + if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) { + // this could happen if a mesh was not created + // (e.g. if it contains unsupported geometry) + fprintf(stderr, "Couldn't find a mesh by UID.\n"); + return NULL; + } + } + if (!uid_mesh_map[*geom_uid]) return NULL; + + Object *ob = add_object(scene, OB_MESH); + + // store object pointer for ArmatureImporter + uid_object_map[*geom_uid] = ob; + + // name Object + const std::string& id = node->getOriginalId(); + if (id.length()) + rename_id(&ob->id, (char*)id.c_str()); + + // replace ob->data freeing the old one + Mesh *old_mesh = (Mesh*)ob->data; + + set_mesh(ob, uid_mesh_map[*geom_uid]); + + if (old_mesh->id.us == 0) free_libblock(&G.main->mesh, old_mesh); + + char layername[100]; + MTFace *texture_face = NULL; + MTex *color_texture = NULL; + + COLLADAFW::MaterialBindingArray& mat_array = + geom->getMaterialBindings(); + + // loop through geom's materials + for (unsigned int i = 0; i < mat_array.getCount(); i++) { + + texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, + &color_texture, layername, texture_face, + material_texture_mapping_map, i); + } + + return ob; +} + +// create a mesh storing a pointer in a map so it can be retrieved later by geometry UID +bool MeshImporter::write_geometry(const COLLADAFW::Geometry* geom) +{ + // TODO: import also uvs, normals + // XXX what to do with normal indices? + // XXX num_normals may be != num verts, then what to do? + + // check geometry->getType() first + if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { + // TODO: report warning + fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType())); + return true; + } + + COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh*)geom; + + if (!is_nice_mesh(mesh)) { + fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh)); + return true; + } + + const std::string& str_geom_id = mesh->getOriginalId(); + Mesh *me = add_mesh((char*)str_geom_id.c_str()); + + // store the Mesh pointer to link it later with an Object + this->uid_mesh_map[mesh->getUniqueId()] = me; + + int new_tris = 0; + + read_vertices(mesh, me); + + new_tris = count_new_tris(mesh, me); + + read_faces(mesh, me, new_tris); + + make_edges(me, 0); + + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); + + return true; +} diff --git a/source/blender/collada/MeshImporter.h b/source/blender/collada/MeshImporter.h new file mode 100644 index 00000000000..1a9f698a7ce --- /dev/null +++ b/source/blender/collada/MeshImporter.h @@ -0,0 +1,156 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file MeshImporter.h + * \ingroup collada + */ + +#ifndef __BC__MESHIMPORTER_H__ +#define __BC__MESHIMPORTER_H__ + +#include <map> +#include <vector> + +#include "COLLADAFWIndexList.h" +#include "COLLADAFWInstanceGeometry.h" +#include "COLLADAFWMaterialBinding.h" +#include "COLLADAFWMesh.h" +#include "COLLADAFWMeshVertexData.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWTextureCoordinateBinding.h" +#include "COLLADAFWTypes.h" +#include "COLLADAFWUniqueId.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "ArmatureImporter.h" +#include "collada_utils.h" + +// only for ArmatureImporter to "see" MeshImporter::get_object_by_geom_uid +class MeshImporterBase +{ +public: + virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) = 0; +}; + +class UVDataWrapper +{ + COLLADAFW::MeshVertexData *mVData; +public: + UVDataWrapper(COLLADAFW::MeshVertexData& vdata); + +#ifdef COLLADA_DEBUG + void print(); +#endif + + void getUV(int uv_index[2], float *uv); +}; + +class MeshImporter : public MeshImporterBase +{ +private: + + UnitConverter *unitconverter; + + Scene *scene; + ArmatureImporter *armature_importer; + + std::map<COLLADAFW::UniqueId, Mesh*> uid_mesh_map; // geometry unique id-to-mesh map + std::map<COLLADAFW::UniqueId, Object*> uid_object_map; // geom uid-to-object + // this structure is used to assign material indices to faces + // it holds a portion of Mesh faces and corresponds to a DAE primitive list (<triangles>, <polylist>, etc.) + struct Primitive { + MFace *mface; + unsigned int totface; + }; + typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap; + std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name! + + + void set_face_indices(MFace *mface, unsigned int *indices, bool quad); + + // not used anymore, test_index_face from blenkernel is better +#if 0 + // change face indices order so that v4 is not 0 + void rotate_face_indices(MFace *mface); +#endif + + void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, + COLLADAFW::IndexList& index_list, unsigned int *tris_indices); + + void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, + COLLADAFW::IndexList& index_list, int index, bool quad); + +#ifdef COLLADA_DEBUG + void print_index_list(COLLADAFW::IndexList& index_list); +#endif + + bool is_nice_mesh(COLLADAFW::Mesh *mesh); + + void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me); + + int triangulate_poly(unsigned int *indices, int totvert, MVert *verts, std::vector<unsigned int>& tri); + + int count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me); + + // TODO: import uv set names + void read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris); + + void get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride); + + bool flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count); + +public: + + MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce); + + virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid); + + MTex *assign_textures_to_uvlayer(COLLADAFW::TextureCoordinateBinding &ctexture, + Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map, + MTex *color_texture); + + MTFace *assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, + std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, + Object *ob, const COLLADAFW::UniqueId *geom_uid, + MTex **color_texture, char *layername, MTFace *texture_face, + std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map, int mat_index); + + + Object *create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom, + bool isController, + std::map<COLLADAFW::UniqueId, Material*>& uid_material_map, + std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map); + + // create a mesh storing a pointer in a map so it can be retrieved later by geometry UID + bool write_geometry(const COLLADAFW::Geometry* geom); + +}; + +#endif diff --git a/source/blender/collada/SConscript b/source/blender/collada/SConscript index a473d31e1de..10c3fcaeb96 100644 --- a/source/blender/collada/SConscript +++ b/source/blender/collada/SConscript @@ -30,11 +30,12 @@ Import ('env') sources = env.Glob('*.cpp') defs = [] +# TODO sanitize inc path building # relative paths to include dirs, space-separated, string if env['OURPLATFORM']=='darwin': - incs = '../blenlib ../blenkernel ../windowmanager ../makesdna ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter [OPENCOLLADA]/COLLADABaseUtils [OPENCOLLADA]/COLLADAFramework [OPENCOLLADA]/COLLADASaxFrameworkLoader '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) + incs = '../blenlib ../blenkernel ../windowmanager ../blenloader ../makesdna ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter [OPENCOLLADA]/COLLADABaseUtils [OPENCOLLADA]/COLLADAFramework [OPENCOLLADA]/COLLADASaxFrameworkLoader '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) else: - incs = '../blenlib ../blenkernel ../windowmanager ../makesdna ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) + incs = '../blenlib ../blenkernel ../windowmanager ../makesdna ../blenloader ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC']) if env['BF_BUILDINFO']: defs.append('NAN_BUILDINFO') diff --git a/source/blender/collada/SkinInfo.cpp b/source/blender/collada/SkinInfo.cpp new file mode 100644 index 00000000000..3e778e09ddf --- /dev/null +++ b/source/blender/collada/SkinInfo.cpp @@ -0,0 +1,331 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <algorithm> + +#if !defined(WIN32) || defined(FREE_WINDOWS) +#include <stdint.h> +#endif + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "BKE_object.h" +#include "DNA_armature_types.h" +#include "DNA_modifier_types.h" +#include "ED_mesh.h" +#include "ED_object.h" +#include "BKE_action.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "SkinInfo.h" +#include "collada_utils.h" + +// use this for retrieving bone names, since these must be unique +template<class T> +static const char *bc_get_joint_name(T *node) +{ + const std::string& id = node->getOriginalId(); + return id.size() ? id.c_str() : node->getName().c_str(); +} + +// This is used to store data passed in write_controller_data. +// Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members +// so that arrays don't get freed until we free them explicitly. +SkinInfo::SkinInfo() {} + +SkinInfo::SkinInfo(const SkinInfo& skin) : weights(skin.weights), + joint_data(skin.joint_data), + unit_converter(skin.unit_converter), + ob_arm(skin.ob_arm), + controller_uid(skin.controller_uid), + parent(skin.parent) +{ + copy_m4_m4(bind_shape_matrix, (float (*)[4])skin.bind_shape_matrix); + + 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::SkinInfo(UnitConverter *conv) : unit_converter(conv), ob_arm(NULL), parent(NULL) {} + +// nobody owns the data after this, so it should be freed manually with releaseMemory +template <class T> +void SkinInfo::transfer_array_data(T& src, T& dest) +{ + 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 SkinInfo::transfer_int_array_data_const(const COLLADAFW::IntValuesArray& src, COLLADAFW::IntValuesArray& dest) +{ + dest.setData((int*)src.getData(), src.getCount()); + dest.yieldOwnerShip(); +} + +void SkinInfo::transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest) +{ + dest.setData((unsigned int*)src.getData(), src.getCount()); + dest.yieldOwnerShip(); +} + +void SkinInfo::borrow_skin_controller_data(const COLLADAFW::SkinControllerData* skin) +{ + transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getJointsPerVertex(), joints_per_vertex); + transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getWeightIndices(), weight_indices); + transfer_array_data((COLLADAFW::IntValuesArray&)skin->getJointIndices(), joint_indices); + // transfer_array_data(skin->getWeights(), weights); + + // cannot transfer data for FloatOrDoubleArray, copy values manually + const COLLADAFW::FloatOrDoubleArray& weight = skin->getWeights(); + for (unsigned int i = 0; i < weight.getValuesCount(); i++) + weights.push_back(bc_get_float_value(weight, i)); + + unit_converter->dae_matrix_to_mat4_(bind_shape_matrix, skin->getBindShapeMatrix()); +} + +void SkinInfo::free() +{ + joints_per_vertex.releaseMemory(); + weight_indices.releaseMemory(); + joint_indices.releaseMemory(); + // weights.releaseMemory(); +} + +// using inverse bind matrices to construct armature +// it is safe to invert them to get the original matrices +// because if they are inverse matrices, they can be inverted +void SkinInfo::add_joint(const COLLADABU::Math::Matrix4& matrix) +{ + JointData jd; + unit_converter->dae_matrix_to_mat4_(jd.inv_bind_mat, matrix); + joint_data.push_back(jd); +} + +void SkinInfo::set_controller(const COLLADAFW::SkinController* co) +{ + controller_uid = co->getUniqueId(); + + // fill in joint UIDs + const COLLADAFW::UniqueIdArray& joint_uids = co->getJoints(); + for (unsigned int i = 0; i < joint_uids.getCount(); i++) { + joint_data[i].joint_uid = joint_uids[i]; + + // // store armature pointer + // JointData& jd = joint_index_to_joint_info_map[i]; + // jd.ob_arm = ob_arm; + + // now we'll be able to get inv bind matrix from joint id + // joint_id_to_joint_index_map[joint_ids[i]] = i; + } +} + +// called from write_controller +Object *SkinInfo::create_armature(Scene *scene) +{ + ob_arm = add_object(scene, OB_ARMATURE); + return ob_arm; +} + +Object* SkinInfo::set_armature(Object *ob_arm) +{ + if (this->ob_arm) + return this->ob_arm; + + this->ob_arm = ob_arm; + return ob_arm; +} + +bool SkinInfo::get_joint_inv_bind_matrix(float inv_bind_mat[][4], COLLADAFW::Node *node) +{ + const COLLADAFW::UniqueId& uid = node->getUniqueId(); + std::vector<JointData>::iterator it; + for (it = joint_data.begin(); it != joint_data.end(); it++) { + if ((*it).joint_uid == uid) { + copy_m4_m4(inv_bind_mat, (*it).inv_bind_mat); + return true; + } + } + + return false; +} + +Object *SkinInfo::get_armature() +{ + return ob_arm; +} + +const COLLADAFW::UniqueId& SkinInfo::get_controller_uid() +{ + return controller_uid; +} + +// check if this skin controller references a joint or any descendant of it +// +// some nodes may not be referenced by SkinController, +// in this case to determine if the node belongs to this armature, +// we need to search down the tree +bool SkinInfo::uses_joint_or_descendant(COLLADAFW::Node *node) +{ + const COLLADAFW::UniqueId& uid = node->getUniqueId(); + std::vector<JointData>::iterator it; + for (it = joint_data.begin(); it != joint_data.end(); it++) { + if ((*it).joint_uid == uid) + return true; + } + + COLLADAFW::NodePointerArray& children = node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (uses_joint_or_descendant(children[i])) + return true; + } + + return false; +} + +void SkinInfo::link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, + TransformReader *tm) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature); + ((ArmatureModifierData *)md)->object = ob_arm; + + copy_m4_m4(ob->obmat, bind_shape_matrix); + object_apply_mat4(ob, ob->obmat, 0, 0); +#if 1 + bc_set_parent(ob, ob_arm, C); +#else + Object workob; + ob->parent = ob_arm; + ob->partype = PAROBJECT; + + what_does_parent(scene, ob, &workob); + invert_m4_m4(ob->parentinv, workob.obmat); + + ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA; + + DAG_scene_sort(bmain, scene); + DAG_ids_flush_update(bmain, 0); + WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); +#endif + + ((bArmature*)ob_arm->data)->deformflag = ARM_DEF_VGROUP; + + // create all vertex groups + std::vector<JointData>::iterator it; + int joint_index; + for (it = joint_data.begin(), joint_index = 0; it != joint_data.end(); it++, joint_index++) { + const char *name = "Group"; + + // skip joints that have invalid UID + if ((*it).joint_uid == COLLADAFW::UniqueId::INVALID) continue; + + // name group by joint node name + + if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) { + name = bc_get_joint_name(joint_by_uid[(*it).joint_uid]); + } + + ED_vgroup_add_name(ob, (char*)name); + } + + // <vcount> - number of joints per vertex - joints_per_vertex + // <v> - [[bone index, weight index] * joints per vertex] * vertices - weight indices + // ^ bone index can be -1 meaning weight toward bind shape, how to express this in Blender? + + // for each vertex in weight indices + // for each bone index in vertex + // add vertex to group at group index + // treat group index -1 specially + + // get def group by index with BLI_findlink + + for (unsigned int vertex = 0, weight = 0; vertex < joints_per_vertex.getCount(); vertex++) { + + unsigned int limit = weight + joints_per_vertex[vertex]; + for ( ; weight < limit; weight++) { + int joint = joint_indices[weight], joint_weight = weight_indices[weight]; + + // -1 means "weight towards the bind shape", we just don't assign it to any group + if (joint != -1) { + bDeformGroup *def = (bDeformGroup*)BLI_findlink(&ob->defbase, joint); + + ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE); + } + } + } +} + +bPoseChannel *SkinInfo::get_pose_channel_from_node(COLLADAFW::Node *node) +{ + return get_pose_channel(ob_arm->pose, bc_get_joint_name(node)); +} + +void SkinInfo::set_parent(Object *_parent) +{ + parent = _parent; +} + +Object* SkinInfo::get_parent() +{ + return parent; +} + +void SkinInfo::find_root_joints(const std::vector<COLLADAFW::Node*> &root_joints, + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, + std::vector<COLLADAFW::Node*>& result) +{ + std::vector<COLLADAFW::Node*>::const_iterator it; + for (it = root_joints.begin(); it != root_joints.end(); it++) { + COLLADAFW::Node *root = *it; + std::vector<JointData>::iterator ji; + for (ji = joint_data.begin(); ji != joint_data.end(); ji++) { + COLLADAFW::Node *joint = joint_by_uid[(*ji).joint_uid]; + if (find_node_in_tree(joint, root)) { + if (std::find(result.begin(), result.end(), root) == result.end()) + result.push_back(root); + } + } + } +} + +bool SkinInfo::find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root) +{ + if (node == tree_root) + return true; + + COLLADAFW::NodePointerArray& children = tree_root->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (find_node_in_tree(node, children[i])) + return true; + } + + return false; +} diff --git a/source/blender/collada/SkinInfo.h b/source/blender/collada/SkinInfo.h new file mode 100644 index 00000000000..42cb7cd99eb --- /dev/null +++ b/source/blender/collada/SkinInfo.h @@ -0,0 +1,137 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file SkinInfo.h + * \ingroup collada + */ + +#ifndef __BC_SKININFO_H__ +#define __BC_SKININFO_H__ + +#include <map> +#include <vector> + +#include "COLLADAFWUniqueId.h" +#include "COLLADAFWTypes.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWSkinController.h" +#include "COLLADAFWSkinControllerData.h" + +#include "DNA_object_types.h" +#include "BKE_context.h" + +#include "TransformReader.h" +#include "collada_internal.h" + +// This is used to store data passed in write_controller_data. +// Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members +// so that arrays don't get freed until we free them explicitly. +class SkinInfo +{ +private: + // to build armature bones from inverse bind matrices + struct JointData { + float inv_bind_mat[4][4]; // joint inverse bind matrix + COLLADAFW::UniqueId joint_uid; // joint node UID + // Object *ob_arm; // armature object + }; + + float bind_shape_matrix[4][4]; + + // data from COLLADAFW::SkinControllerData, each array should be freed + COLLADAFW::UIntValuesArray joints_per_vertex; + COLLADAFW::UIntValuesArray weight_indices; + COLLADAFW::IntValuesArray joint_indices; + // COLLADAFW::FloatOrDoubleArray weights; + std::vector<float> weights; + + std::vector<JointData> joint_data; // index to this vector is joint index + + UnitConverter *unit_converter; + + Object *ob_arm; + COLLADAFW::UniqueId controller_uid; + Object *parent; + +public: + + SkinInfo(); + SkinInfo(const SkinInfo& skin); + SkinInfo(UnitConverter *conv); + + // nobody owns the data after this, so it should be freed manually with releaseMemory + template <typename T> + void transfer_array_data(T& src, T& dest); + + // when src is const we cannot src.yieldOwnerShip, this is used by copy constructor + void transfer_int_array_data_const(const COLLADAFW::IntValuesArray& src, COLLADAFW::IntValuesArray& dest); + + void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest); + + void borrow_skin_controller_data(const COLLADAFW::SkinControllerData* skin); + + void free(); + + // using inverse bind matrices to construct armature + // it is safe to invert them to get the original matrices + // because if they are inverse matrices, they can be inverted + void add_joint(const COLLADABU::Math::Matrix4& matrix); + + void set_controller(const COLLADAFW::SkinController* co); + + // called from write_controller + Object *create_armature(Scene *scene); + + Object* set_armature(Object *ob_arm); + + bool get_joint_inv_bind_matrix(float inv_bind_mat[][4], COLLADAFW::Node *node); + + Object *get_armature(); + + const COLLADAFW::UniqueId& get_controller_uid(); + + // check if this skin controller references a joint or any descendant of it + // + // some nodes may not be referenced by SkinController, + // in this case to determine if the node belongs to this armature, + // we need to search down the tree + bool uses_joint_or_descendant(COLLADAFW::Node *node); + + void link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, TransformReader *tm); + + bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node); + + void set_parent(Object *_parent); + + Object* get_parent(); + + void find_root_joints(const std::vector<COLLADAFW::Node*> &root_joints, + std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid, + std::vector<COLLADAFW::Node*>& result); + + bool find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root); + +}; + +#endif diff --git a/source/blender/collada/TransformReader.cpp b/source/blender/collada/TransformReader.cpp new file mode 100644 index 00000000000..04711775f14 --- /dev/null +++ b/source/blender/collada/TransformReader.cpp @@ -0,0 +1,127 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "TransformReader.h" + +TransformReader::TransformReader(UnitConverter* conv) : unit_converter(conv) {} + +void TransformReader::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]; + + unit_m4(mat); + + for (unsigned 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: + dae_translate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::ROTATE: + dae_rotate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::SCALE: + dae_scale_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::MATRIX: + dae_matrix_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::LOOKAT: + case COLLADAFW::Transformation::SKEW: + fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n"); + break; + } + + copy_m4_m4(copy, mat); + mul_m4_m4m4(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; + } + } +} + +void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) +{ + COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm; + COLLADABU::Math::Vector3& axis = ro->getRotationAxis(); + float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f); + float ax[] = {axis[0], axis[1], axis[2]}; + // float quat[4]; + // axis_angle_to_quat(quat, axis, angle); + // quat_to_mat4(m, quat); + axis_angle_to_mat4(m, ax, angle); +} + +void TransformReader::dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) +{ + COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm; + COLLADABU::Math::Vector3& t = tra->getTranslation(); + + unit_m4(m); + + m[3][0] = (float)t[0]; + m[3][1] = (float)t[1]; + m[3][2] = (float)t[2]; +} + +void TransformReader::dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) +{ + COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale(); + float size[3] = {(float)s[0], (float)s[1], (float)s[2]}; + size_to_mat4(m, size); +} + +void TransformReader::dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[][4]) +{ + unit_converter->dae_matrix_to_mat4_(m, ((COLLADAFW::Matrix*)tm)->getMatrix()); +} + +void TransformReader::dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]) +{ + dae_vector3_to_v3(((COLLADAFW::Translate*)tm)->getTranslation(), v); +} + +void TransformReader::dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]) +{ + dae_vector3_to_v3(((COLLADAFW::Scale*)tm)->getScale(), v); +} + +void TransformReader::dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]) +{ + v[0] = v3.x; + v[1] = v3.y; + v[2] = v3.z; +} diff --git a/source/blender/collada/TransformReader.h b/source/blender/collada/TransformReader.h new file mode 100644 index 00000000000..3997d8bb1d7 --- /dev/null +++ b/source/blender/collada/TransformReader.h @@ -0,0 +1,74 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file TransformReader.h + * \ingroup collada + */ + +#ifndef __BC_TRANSFORMREADER_H__ +#define __BC_TRANSFORMREADER_H__ + +#include "COLLADAFWNode.h" +#include "COLLADAFWTransformation.h" +#include "COLLADAFWTranslate.h" +#include "COLLADAFWRotate.h" +#include "COLLADAFWScale.h" +#include "COLLADAFWMatrix.h" +#include "COLLADAFWUniqueId.h" +#include "Math/COLLADABUMathVector3.h" + +#include "DNA_object_types.h" +#include "BLI_math.h" + +#include "collada_internal.h" + +//struct Object; + +class TransformReader : public TransformBase +{ +protected: + + UnitConverter *unit_converter; + +public: + struct Animation { + Object *ob; + COLLADAFW::Node *node; + COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id + }; + + TransformReader(UnitConverter* conv); + + void get_node_mat(float mat[][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map, Object *ob); + + void dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]); + void dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[][4]); + void dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[][4]); + void dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[][4]); + void dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]); + void dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]); + void dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]); +}; + +#endif diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp new file mode 100644 index 00000000000..c74f23599e8 --- /dev/null +++ b/source/blender/collada/TransformWriter.cpp @@ -0,0 +1,100 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "BKE_object.h" + +#include "TransformWriter.h" + +#include "BLI_math.h" + +void TransformWriter::add_node_transform(COLLADASW::Node& node, float mat[][4], float parent_mat[][4]) +{ + float loc[3], rot[3], scale[3]; + float local[4][4]; + + if (parent_mat) { + float invpar[4][4]; + invert_m4_m4(invpar, parent_mat); + mul_m4_m4m4(local, mat, invpar); + } + else { + copy_m4_m4(local, mat); + } + + TransformBase::decompose(local, loc, rot, NULL, scale); + + add_transform(node, loc, rot, scale); +} + +void TransformWriter::add_node_transform_ob(COLLADASW::Node& node, Object *ob) +{ + float rot[3], loc[3], scale[3]; + + if (ob->parent) { + float C[4][4], tmat[4][4], imat[4][4], mat[4][4]; + + // factor out scale from obmat + + copy_v3_v3(scale, ob->size); + + ob->size[0] = ob->size[1] = ob->size[2] = 1.0f; + object_to_mat4(ob, C); + copy_v3_v3(ob->size, scale); + + mul_serie_m4(tmat, ob->parent->obmat, ob->parentinv, C, NULL, NULL, NULL, NULL, NULL); + + // calculate local mat + + invert_m4_m4(imat, ob->parent->obmat); + mul_m4_m4m4(mat, tmat, imat); + + // done + + mat4_to_eul(rot, mat); + copy_v3_v3(loc, mat[3]); + } + else { + copy_v3_v3(loc, ob->loc); + copy_v3_v3(rot, ob->rot); + copy_v3_v3(scale, ob->size); + } + + add_transform(node, loc, rot, scale); +} + +void TransformWriter::add_node_transform_identity(COLLADASW::Node& node) +{ + float loc[] = {0.0f, 0.0f, 0.0f}, scale[] = {1.0f, 1.0f, 1.0f}, rot[] = {0.0f, 0.0f, 0.0f}; + add_transform(node, loc, rot, scale); +} + +void TransformWriter::add_transform(COLLADASW::Node& node, float loc[3], float rot[3], float scale[3]) +{ + 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", scale[0], scale[1], scale[2]); +} diff --git a/source/blender/collada/TransformWriter.h b/source/blender/collada/TransformWriter.h new file mode 100644 index 00000000000..a0cda4c464f --- /dev/null +++ b/source/blender/collada/TransformWriter.h @@ -0,0 +1,52 @@ +/* + * $Id$ + * + * ***** 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, + * Nathan Letwory + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file TransformWriter.h + * \ingroup collada + */ + +#ifndef __TRANSFORMWRITER_H__ +#define __TRANSFORMWRITER_H__ + +#include "COLLADASWNode.h" + +#include "DNA_object_types.h" + +#include "collada_internal.h" + +class TransformWriter : protected TransformBase +{ +protected: + void add_node_transform(COLLADASW::Node& node, float mat[][4], float parent_mat[][4]); + + void add_node_transform_ob(COLLADASW::Node& node, Object *ob); + + void add_node_transform_identity(COLLADASW::Node& node); + +private: + void add_transform(COLLADASW::Node& node, float loc[3], float rot[3], float scale[3]); +}; + +#endif diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index a519db3332c..e285197f8f7 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -21,19 +21,22 @@ * * ***** END GPL LICENSE BLOCK ***** */ -#include "BKE_main.h" -#include "BKE_scene.h" -#include "BKE_context.h" + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" #include "DocumentExporter.h" #include "DocumentImporter.h" extern "C" { +#include "BKE_scene.h" +#include "BKE_context.h" + int collada_import(bContext *C, const char *filepath) { - DocumentImporter imp; - imp.import(C, filepath); + DocumentImporter imp (C, filepath); + imp.import(); return 1; } diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index 1c724bef6a6..a167784e217 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -21,6 +21,11 @@ * * ***** END GPL LICENSE BLOCK ***** */ + +/** \file collada.h + * \ingroup collada + */ + #ifndef BLENDER_COLLADA_H #define BLENDER_COLLADA_H diff --git a/source/blender/collada/collada_internal.cpp b/source/blender/collada/collada_internal.cpp new file mode 100644 index 00000000000..b3ac62bb6a4 --- /dev/null +++ b/source/blender/collada/collada_internal.cpp @@ -0,0 +1,269 @@ +/* + * $Id$ + * + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "collada_internal.h" + +UnitConverter::UnitConverter() : unit(), up_axis(COLLADAFW::FileInfo::Z_UP) {} + +void UnitConverter::read_asset(const COLLADAFW::FileInfo* asset) +{ + unit = asset->getUnit(); + up_axis = asset->getUpAxisType(); +} + +UnitConverter::UnitSystem UnitConverter::isMetricSystem() +{ + switch(unit.getLinearUnitUnit()) { + case COLLADAFW::FileInfo::Unit::MILLIMETER: + case COLLADAFW::FileInfo::Unit::CENTIMETER: + case COLLADAFW::FileInfo::Unit::DECIMETER: + case COLLADAFW::FileInfo::Unit::METER: + case COLLADAFW::FileInfo::Unit::KILOMETER: + return UnitConverter::Metric; + case COLLADAFW::FileInfo::Unit::INCH: + case COLLADAFW::FileInfo::Unit::FOOT: + case COLLADAFW::FileInfo::Unit::YARD: + return UnitConverter::Imperial; + default: + return UnitConverter::None; + } +} + +float UnitConverter::getLinearMeter() +{ + return (float)unit.getLinearUnitMeter(); +} + +void UnitConverter::convertVector3(COLLADABU::Math::Vector3 &vec, float *v) +{ + v[0] = vec.x; + v[1] = vec.y; + v[2] = vec.z; +} + +// TODO need also for angle conversion, time conversion... + +void UnitConverter::dae_matrix_to_mat4_(float out[][4], const COLLADABU::Math::Matrix4& in) +{ + // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h) + // so here, to make a blender matrix, we swap columns and rows + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + out[i][j] = in[j][i]; + } + } +} + +void UnitConverter::mat4_to_dae(float out[][4], float in[][4]) +{ + copy_m4_m4(out, in); + transpose_m4(out); +} + +void UnitConverter::mat4_to_dae_double(double out[][4], float in[][4]) +{ + float mat[4][4]; + + mat4_to_dae(mat, in); + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + out[i][j] = mat[i][j]; +} + +void TransformBase::decompose(float mat[][4], float *loc, float eul[3], float quat[4], float *size) +{ + mat4_to_size(size, mat); + if (eul) { + mat4_to_eul(eul, mat); + } + if (quat) { + mat4_to_quat(quat, mat); + } + copy_v3_v3(loc, mat[3]); +} + +/** +Translation map. +Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be +included. Look at the IDREF XSD declaration for more. +Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, +like special chars (e.g. micro sign), umlauts and so on. +The COLLADA spec also allows additional chars for member access ('.'), these +must obviously be removed too, otherwise they would be heavily misinterpreted. +*/ +const unsigned char translate_start_name_map[256] = { +95, 95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +65, 66, 67, 68, 69, 70, 71, 72, +73, 74, 75, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, +89, 90, 95, 95, 95, 95, 95, 95, +97, 98, 99, 100, 101, 102, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, +113, 114, 115, 116, 117, 118, 119, 120, +121, 122, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 192, +193, 194, 195, 196, 197, 198, 199, 200, +201, 202, 203, 204, 205, 206, 207, 208, +209, 210, 211, 212, 213, 214, 95, 216, +217, 218, 219, 220, 221, 222, 223, 224, +225, 226, 227, 228, 229, 230, 231, 232, +233, 234, 235, 236, 237, 238, 239, 240, +241, 242, 243, 244, 245, 246, 95, 248, +249, 250, 251, 252, 253, 254, 255}; + +const unsigned char translate_name_map[256] = { +95, 95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 45, 95, 95, 48, +49, 50, 51, 52, 53, 54, 55, 56, +57, 95, 95, 95, 95, 95, 95, 95, +65, 66, 67, 68, 69, 70, 71, 72, +73, 74, 75, 76, 77, 78, 79, 80, +81, 82, 83, 84, 85, 86, 87, 88, +89, 90, 95, 95, 95, 95, 95, 95, +97, 98, 99, 100, 101, 102, 103, 104, +105, 106, 107, 108, 109, 110, 111, 112, +113, 114, 115, 116, 117, 118, 119, 120, +121, 122, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 95, 95, +95, 95, 95, 95, 95, 95, 183, 95, +95, 95, 95, 95, 95, 95, 95, 192, +193, 194, 195, 196, 197, 198, 199, 200, +201, 202, 203, 204, 205, 206, 207, 208, +209, 210, 211, 212, 213, 214, 95, 216, +217, 218, 219, 220, 221, 222, 223, 224, +225, 226, 227, 228, 229, 230, 231, 232, +233, 234, 235, 236, 237, 238, 239, 240, +241, 242, 243, 244, 245, 246, 95, 248, +249, 250, 251, 252, 253, 254, 255}; + +typedef std::map< std::string, std::vector<std::string> > map_string_list; +map_string_list global_id_map; + +void clear_global_id_map() +{ + global_id_map.clear(); +} + +/** Look at documentation of translate_map */ +std::string translate_id(const std::string &id) +{ + if (id.size() == 0) + { return id; } + std::string id_translated = id; + id_translated[0] = translate_start_name_map[(unsigned int)id_translated[0]]; + for (unsigned int i=1; i < id_translated.size(); i++) + { + id_translated[i] = translate_name_map[(unsigned int)id_translated[i]]; + } + // It's so much workload now, the if() should speed up things. + if (id_translated != id) + { + // Search duplicates + map_string_list::iterator iter = global_id_map.find(id_translated); + if (iter != global_id_map.end()) + { + unsigned int i = 0; + bool found = false; + for (i=0; i < iter->second.size(); i++) + { + if (id == iter->second[i]) + { + found = true; + break; + } + } + bool convert = false; + if (found) + { + if (i > 0) + { convert = true; } + } + else + { + convert = true; + global_id_map[id_translated].push_back(id); + } + if (convert) + { + std::stringstream out; + out << ++i; + id_translated += out.str(); + } + } + else { global_id_map[id_translated].push_back(id); } + } + return id_translated; +} + +std::string id_name(void *id) +{ + return ((ID*)id)->name + 2; +} + +std::string get_geometry_id(Object *ob) +{ + return translate_id(id_name(ob->data)) + "-mesh"; +} + +std::string get_light_id(Object *ob) +{ + return translate_id(id_name(ob)) + "-light"; +} + +std::string get_joint_id(Bone *bone, Object *ob_arm) +{ + return translate_id(id_name(ob_arm) + "_" + bone->name); +} + +std::string get_camera_id(Object *ob) +{ + return translate_id(id_name(ob)) + "-camera"; +} diff --git a/source/blender/collada/collada_internal.h b/source/blender/collada/collada_internal.h index 242fce749c4..815113400b4 100644 --- a/source/blender/collada/collada_internal.h +++ b/source/blender/collada/collada_internal.h @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -21,12 +21,25 @@ * * ***** END GPL LICENSE BLOCK ***** */ -#ifndef BLENDER_COLLADA_H -#define BLENDER_COLLADA_H + +/** \file collada_internal.h + * \ingroup collada + */ + +#ifndef COLLADA_INTERNAL_H +#define COLLADA_INTERNAL_H + +#include <string> +#include <vector> +#include <map> #include "COLLADAFWFileInfo.h" #include "Math/COLLADABUMathMatrix4.h" +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "BLI_math.h" + class UnitConverter { private: @@ -35,61 +48,50 @@ private: public: - UnitConverter() : unit(), up_axis(COLLADAFW::FileInfo::Z_UP) {} + enum UnitSystem { + None, + Metric, + Imperial + }; + + // Initialize with Z_UP, since Blender uses right-handed, z-up + UnitConverter(); - void read_asset(const COLLADAFW::FileInfo* asset) - { - } + void read_asset(const COLLADAFW::FileInfo* asset); - // TODO - // convert vector vec from COLLADA format to Blender - void convertVec3(float *vec) - { - } + void convertVector3(COLLADABU::Math::Vector3 &vec, float *v); + + UnitConverter::UnitSystem isMetricSystem(void); + + float getLinearMeter(void); // TODO need also for angle conversion, time conversion... - void dae_matrix_to_mat4(float out[][4], const COLLADABU::Math::Matrix4& in) - { - // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h) - // so here, to make a blender matrix, we swap columns and rows - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - out[i][j] = in[j][i]; - } - } - } - - void mat4_to_dae(float out[][4], float in[][4]) - { - copy_m4_m4(out, in); - transpose_m4(out); - } - - void mat4_to_dae_double(double out[][4], float in[][4]) - { - float mat[4][4]; - - mat4_to_dae(mat, in); - - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - out[i][j] = mat[i][j]; - } + void dae_matrix_to_mat4_(float out[][4], const COLLADABU::Math::Matrix4& in); + + void mat4_to_dae(float out[][4], float in[][4]); + + void mat4_to_dae_double(double out[][4], float in[][4]); }; class TransformBase { public: - void decompose(float mat[][4], float *loc, float eul[3], float quat[4], float *size) - { - mat4_to_size(size, mat); - if (eul) - mat4_to_eul(eul, mat); - if (quat) - mat4_to_quat(quat, mat); - copy_v3_v3(loc, mat[3]); - } + void decompose(float mat[][4], float *loc, float eul[3], float quat[4], float *size); }; -#endif +extern void clear_global_id_map(); +/** Look at documentation of translate_map */ +extern std::string translate_id(const std::string &id); + +extern std::string id_name(void *id); + +extern std::string get_geometry_id(Object *ob); + +extern std::string get_light_id(Object *ob); + +extern std::string get_joint_id(Bone *bone, Object *ob_arm); + +extern std::string get_camera_id(Object *ob); + +#endif /* COLLADA_INTERNAL_H */ diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp new file mode 100644 index 00000000000..31ef8064b1f --- /dev/null +++ b/source/blender/collada/collada_utils.cpp @@ -0,0 +1,109 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "COLLADAFWGeometry.h" +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWMeshVertexData.h" + +#include "DNA_customdata_types.h" +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_object.h" + +#include "WM_api.h" // XXX hrm, see if we can do without this +#include "WM_types.h" + +float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index) +{ + if (index >= array.getValuesCount()) + return 0.0f; + + if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) + return array.getFloatValues()->getData()[index]; + else + return array.getDoubleValues()->getData()[index]; +} + +// copied from /editors/object/object_relations.c +int bc_test_parent_loop(Object *par, Object *ob) +{ + /* test if 'ob' is a parent somewhere in par's parents */ + + if(par == NULL) return 0; + if(ob == par) return 1; + + return bc_test_parent_loop(par->parent, ob); +} + +// a shortened version of parent_set_exec() +// if is_parent_space is true then ob->obmat will be multiplied by par->obmat before parenting +int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space) +{ + Object workob; + Main *bmain = CTX_data_main(C); + Scene *sce = CTX_data_scene(C); + + if (!par || bc_test_parent_loop(par, ob)) + return false; + + ob->parent = par; + ob->partype = PAROBJECT; + + ob->parsubstr[0] = 0; + + if (is_parent_space) { + float mat[4][4]; + // calc par->obmat + where_is_object(sce, par); + + // move child obmat into world space + mul_m4_m4m4(mat, ob->obmat, par->obmat); + copy_m4_m4(ob->obmat, mat); + } + + // apply child obmat (i.e. decompose it into rot/loc/size) + object_apply_mat4(ob, ob->obmat, 0, 0); + + // compute parentinv + what_does_parent(sce, ob, &workob); + invert_m4_m4(ob->parentinv, workob.obmat); + + ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA; + par->recalc |= OB_RECALC_OB; + + DAG_scene_sort(bmain, sce); + DAG_ids_flush_update(bmain, 0); + WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); + + return true; +} + diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h new file mode 100644 index 00000000000..06cf8259dac --- /dev/null +++ b/source/blender/collada/collada_utils.h @@ -0,0 +1,54 @@ +/* + * $Id$ + * + * ***** 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, Nathan Letwory. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file collada_utils.h + * \ingroup collada + */ + +#ifndef __BC_UTILS_H__ +#define __BC_UTILS_H__ + +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWGeometry.h" +#include "COLLADAFWFloatOrDoubleArray.h" +#include "COLLADAFWTypes.h" + +#include <vector> +#include <map> + +#include "DNA_object_types.h" +#include "DNA_customdata_types.h" +#include "DNA_texture_types.h" +#include "BKE_context.h" + +typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex*> > TexIndexTextureArrayMap; + +extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray& array, unsigned int index); + +extern int bc_test_parent_loop(Object *par, Object *ob); +extern int bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space=true); +extern char *bc_CustomData_get_layer_name(const CustomData *data, int type, int n); +extern char *bc_CustomData_get_active_layer_name(const CustomData *data, int type); + +#endif |