Пример #1
0
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);
			}
		}
	}
}
std::string get_geometry_id(Object *ob)
{
	return translate_id(id_name(ob->data)) + "-mesh";
}
/** Look at documentation of translate_map */
std::string translate_id(const char *idString)
{
	std::string id = std::string(idString);
	return translate_id(id);
}
std::string ControllerExporter::get_controller_id(Key *key, Object *ob)
{
	return translate_id(id_name(ob)) + MORPH_CONTROLLER_ID_SUFFIX;
}
Пример #5
0
// powerful because it handles both cases when there is material and when there's not
void GeometryExporter::createPolylist(short material_index,
                                      bool has_uvs,
                                      bool has_color,
                                      Object *ob,
                                      Mesh *me,
                                      std::string& geom_id,
                                      std::vector<BCPolygonNormalsIndices>& norind)
{

	MPoly *mpolys = me->mpoly;
	MLoop *mloops = me->mloop;
	int totpolys  = me->totpoly;

	// <vcount>
	int i;
	int faces_in_polylist = 0;
	std::vector<unsigned long> vcount_list;

	// count faces with this material
	for (i = 0; i < totpolys; i++) {
		MPoly *p = &mpolys[i];
		
		if (p->mat_nr == material_index) {
			faces_in_polylist++;
			vcount_list.push_back(p->totloop);
		}
	}

	// no faces using this material
	if (faces_in_polylist == 0) {
		fprintf(stderr, "%s: material with index %d is not used.\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) {
		std::string material_id = get_material_id(ma);
		std::ostringstream ostr;
		ostr << translate_id(material_id);
		polylist.setMaterial(ostr.str());
	}
			
	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);
	int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE)-1;
	for (i = 0; i < num_layers; i++) {
		if (!this->export_settings->active_uv_only || i == active_uv_index) {

			// char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
			COLLADASW::Input input3(COLLADASW::InputSemantic::TEXCOORD,
									makeUrl(makeTexcoordSourceId(geom_id, i, this->export_settings->active_uv_only)),
									2, // this is only until we have optimized UV sets
									(this->export_settings->active_uv_only) ? 0 : i  // only_active_uv exported -> we have only one set
									);
			til.push_back(input3);
		}
	}

	int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL);
	if (totlayer_mcol > 0) {
		int map_index = 0;

		for (int a = 0; a < totlayer_mcol; a++) {
			char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a);
			COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR,
			                        makeUrl(makeVertexColorSourceId(geom_id, layer_name)),
			                        (has_uvs) ? 3 : 2,  // all color layers have same index order
			                        map_index           // set number equals color map index
			                        );
			til.push_back(input4);
			map_index++;
		}
	}
		
	// sets <vcount>
	polylist.setVCountList(vcount_list);
		
	// performs the actual writing
	polylist.prepareToAppendValues();
	
	// <p>
	int texindex = 0;
	for (i = 0; i < totpolys; i++) {
		MPoly *p = &mpolys[i];
		int loop_count = p->totloop;

		if (p->mat_nr == material_index) {
			MLoop *l = &mloops[p->loopstart];
			BCPolygonNormalsIndices normal_indices = norind[i];

			for (int j = 0; j < loop_count; j++) {
				polylist.appendValues(l[j].v);
				polylist.appendValues(normal_indices[j]);
				if (has_uvs)
					polylist.appendValues(texindex + j);

				if (has_color)
					polylist.appendValues(texindex + j);
			}
		}

		texindex += loop_count;
	}
		
	polylist.finish();
}
Пример #6
0
void SceneExporter::writeNodes(Object *ob, Scene *sce)
{
	// Add associated armature first if available
	bool armature_exported = false;
	Object *ob_arm = bc_get_assigned_armature(ob);
	if (ob_arm != NULL) {
		armature_exported = bc_is_in_Export_set(this->export_settings->export_set, ob_arm);
		if (armature_exported && bc_is_marked(ob_arm)) {
			bc_remove_mark(ob_arm);
			writeNodes(ob_arm, sce);
			armature_exported = true;
		}
	}

	COLLADASW::Node colladaNode(mSW);
	colladaNode.setNodeId(translate_id(id_name(ob)));
	colladaNode.setNodeName(translate_id(id_name(ob)));
	colladaNode.setType(COLLADASW::Node::NODE);

	colladaNode.start();

	std::list<Object *> child_objects;

	// list child objects
	LinkNode *node;
	for (node=this->export_settings->export_set; node; node=node->next) {
		// cob - child object
		Object *cob = (Object *)node->link;

		if (cob->parent == ob) {
			switch (cob->type) {
				case OB_MESH:
				case OB_CAMERA:
				case OB_LAMP:
				case OB_EMPTY:
				case OB_ARMATURE:
					if (bc_is_marked(cob))
						child_objects.push_back(cob);
					break;
			}
		}
	}

	if (ob->type == OB_MESH && armature_exported)
		// for skinned mesh we write obmat in <bind_shape_matrix>
		TransformWriter::add_node_transform_identity(colladaNode);
	else
		TransformWriter::add_node_transform_ob(colladaNode, ob);

	// <instance_geometry>
	if (ob->type == OB_MESH) {
		bool instance_controller_created = false;
		if (armature_exported) {
			instance_controller_created = arm_exporter->add_instance_controller(ob);
		}
		if (!instance_controller_created) {
			COLLADASW::InstanceGeometry instGeom(mSW);
			instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, this->export_settings->use_object_instantiation)));

			InstanceWriter::add_material_bindings(instGeom.getBindMaterial(), ob, this->export_settings->active_uv_only);

			instGeom.add();
		}
	}

	// <instance_controller>
	else if (ob->type == OB_ARMATURE) {
		arm_exporter->add_armature_bones(ob, sce, this, child_objects);
	}

	// <instance_camera>
	else if (ob->type == OB_CAMERA) {
		COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob)));
		instCam.add();
	}

	// <instance_light>
	else if (ob->type == OB_LAMP) {
		COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob)));
		instLa.add();
	}

	// empty object
	else if (ob->type == OB_EMPTY) { // TODO: handle groups (OB_DUPLIGROUP
		if ((ob->transflag & OB_DUPLIGROUP) == OB_DUPLIGROUP && ob->dup_group) {
			GroupObject *go = NULL;
			Group *gr = ob->dup_group;
			/* printf("group detected '%s'\n", gr->id.name+2); */
			for (go = (GroupObject *)(gr->gobject.first); go; go = go->next) {
				printf("\t%s\n", go->ob->id.name);
			}
		}
	}

	if (ob->type == OB_ARMATURE) {
		colladaNode.end();
	}

	for (std::list<Object *>::iterator i = child_objects.begin(); i != child_objects.end(); ++i) {
		if (bc_is_marked(*i)) {
			bc_remove_mark(*i);
			writeNodes(*i, sce);
		}
	}

	if (ob->type != OB_ARMATURE) {
		colladaNode.end();
	}
}
std::string get_material_id(Material *mat)
{
	return translate_id(id_name(mat)) + "-material";
}
std::string get_joint_id(Bone *bone, Object *ob_arm)
{
	return translate_id(/*id_name(ob_arm) + "_" +*/ bone->name);
}
Пример #9
0
void DocumentExporter::exportCurrentScene(Scene *sce)
{
	PointerRNA sceneptr, unit_settings;
	PropertyRNA *system; /* unused , *scale; */

	clear_global_id_map();
	
	COLLADABU::NativeString native_filename =
	    COLLADABU::NativeString(std::string(this->export_settings->filepath), COLLADABU::NativeString::ENCODING_UTF8);
	COLLADASW::StreamWriter sw(native_filename);

	fprintf(stdout, "Collada export: %s\n", this->export_settings->filepath);

	// open <collada>
	sw.startDocument();

	// <asset>
	COLLADASW::Asset asset(&sw);

	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 = 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);
	if (U.author[0] != '\0') {
		asset.getContributor().mAuthor = U.author;
	}
	else {
		asset.getContributor().mAuthor = "Blender User";
	}
	char version_buf[128];
#ifdef WITH_BUILDINFO
	sprintf(version_buf, "Blender %d.%02d.%d r%s", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION, build_rev);
#else
	sprintf(version_buf, "Blender %d.%02d.%d", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION);
#endif
	asset.getContributor().mAuthoringTool = version_buf;
	asset.add();
	
	LinkNode *export_set = this->export_settings->export_set;
	// <library_cameras>
	if (bc_has_object_type(export_set, OB_CAMERA)) {
		CamerasExporter ce(&sw, this->export_settings);
		ce.exportCameras(sce);
	}
	
	// <library_lights>
	if (bc_has_object_type(export_set, OB_LAMP)) {
		LightsExporter le(&sw, this->export_settings);
		le.exportLights(sce);
	}

	// <library_images>
	ImagesExporter ie(&sw, this->export_settings);
	ie.exportImages(sce);
	
	// <library_effects>
	EffectsExporter ee(&sw, this->export_settings);
	ee.exportEffects(sce);
	
	// <library_materials>
	MaterialsExporter me(&sw, this->export_settings);
	me.exportMaterials(sce);

	// <library_geometries>
	if (bc_has_object_type(export_set, OB_MESH)) {
		GeometryExporter ge(&sw, this->export_settings);
		ge.exportGeom(sce);
	}

	// <library_animations>
	AnimationExporter ae(&sw, this->export_settings);
	ae.exportAnimations(sce);

	// <library_controllers>
	ArmatureExporter arm_exporter(&sw, this->export_settings);
	if (bc_has_object_type(export_set, OB_ARMATURE)) {
		arm_exporter.export_controllers(sce);
	}

	// <library_visual_scenes>
	SceneExporter se(&sw, &arm_exporter, this->export_settings);
	se.exportScene(sce);
	
	// <scene>
	std::string scene_name(translate_id(id_name(sce)));
	COLLADASW::Scene scene(&sw, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING,
	                                           scene_name));
	scene.add();
	
	// close <Collada>
	sw.endDocument();

}
Пример #10
0
void SceneExporter::writeNodes(Object *ob, Scene *sce)
{
	// Add associated armature first if available
	bool armature_exported = false;
	Object *ob_arm = bc_get_assigned_armature(ob);
	if (ob_arm != NULL) {
		armature_exported = bc_is_in_Export_set(this->export_settings->export_set, ob_arm);
		if (armature_exported && bc_is_marked(ob_arm)) {
			bc_remove_mark(ob_arm);
			writeNodes(ob_arm, sce);
			armature_exported = true;
		}
	}

	COLLADASW::Node colladaNode(mSW);
	colladaNode.setNodeId(translate_id(id_name(ob)));
	colladaNode.setNodeName(translate_id(id_name(ob)));
	colladaNode.setType(COLLADASW::Node::NODE);

	colladaNode.start();

	std::list<Object *> child_objects;

	// list child objects
	LinkNode *node;
	for (node=this->export_settings->export_set; node; node=node->next) {
		// cob - child object
		Object *cob = (Object *)node->link;

		if (cob->parent == ob) {
			switch (cob->type) {
				case OB_MESH:
				case OB_CAMERA:
				case OB_LAMP:
				case OB_EMPTY:
				case OB_ARMATURE:
					if (bc_is_marked(cob))
						child_objects.push_back(cob);
					break;
			}
		}
	}

	if (ob->type == OB_MESH && armature_exported)
		// for skinned mesh we write obmat in <bind_shape_matrix>
		TransformWriter::add_node_transform_identity(colladaNode);
	else {
		TransformWriter::add_node_transform_ob(colladaNode, ob, this->transformation_type);
	}

	// <instance_geometry>
	if (ob->type == OB_MESH) {
		bool instance_controller_created = false;
		if (armature_exported) {
			instance_controller_created = arm_exporter->add_instance_controller(ob);
		}
		if (!instance_controller_created) {
			COLLADASW::InstanceGeometry instGeom(mSW);
			instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, this->export_settings->use_object_instantiation)));
			instGeom.setName(translate_id(id_name(ob)));
			InstanceWriter::add_material_bindings(instGeom.getBindMaterial(), ob, this->export_settings->active_uv_only);

			instGeom.add();
		}
	}

	// <instance_controller>
	else if (ob->type == OB_ARMATURE) {
		arm_exporter->add_armature_bones(ob, sce, this, child_objects);
	}

	// <instance_camera>
	else if (ob->type == OB_CAMERA) {
		COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob)));
		instCam.add();
	}

	// <instance_light>
	else if (ob->type == OB_LAMP) {
		COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob)));
		instLa.add();
	}

	// empty object
	else if (ob->type == OB_EMPTY) { // TODO: handle groups (OB_DUPLIGROUP
		if ((ob->transflag & OB_DUPLIGROUP) == OB_DUPLIGROUP && ob->dup_group) {
			GroupObject *go = NULL;
			Group *gr = ob->dup_group;
			/* printf("group detected '%s'\n", gr->id.name + 2); */
			for (go = (GroupObject *)(gr->gobject.first); go; go = go->next) {
				printf("\t%s\n", go->ob->id.name);
			}
		}
	}

	if (ob->type == OB_ARMATURE) {
		colladaNode.end();
	}

	if (BLI_listbase_is_empty(&ob->constraints) == false) {
		bConstraint *con = (bConstraint *) ob->constraints.first;
		while (con) {
			std::string con_name(translate_id(con->name));
			std::string con_tag = con_name + "_constraint";
			printf("%s\n", con_name.c_str());
			printf("%s\n\n", con_tag.c_str());
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"type",con->type);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"enforce",con->enforce);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"flag",con->flag);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"headtail",con->headtail);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"lin_error",con->lin_error);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"own_space",con->ownspace);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"rot_error",con->rot_error);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"tar_space",con->tarspace);
			colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"lin_error",con->lin_error);
			
			//not ideal: add the target object name as another parameter. 
			//No real mapping in the .dae
			//Need support for multiple target objects also.
			const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
			ListBase targets = {NULL, NULL};
			if (cti && cti->get_constraint_targets) {
			
				bConstraintTarget *ct;
				Object *obtar;
			
				cti->get_constraint_targets(con, &targets);

				for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
					obtar = ct->tar;
					std::string tar_id((obtar) ? id_name(obtar) : "");
					colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"target_id",tar_id);
				}

				if (cti->flush_constraint_targets)
					cti->flush_constraint_targets(con, &targets, 1);

			}

			con = con->next;
		}
	}

	for (std::list<Object *>::iterator i = child_objects.begin(); i != child_objects.end(); ++i) {
		if (bc_is_marked(*i)) {
			bc_remove_mark(*i);
			writeNodes(*i, sce);
		}
	}

	if (ob->type != OB_ARMATURE)
		colladaNode.end();
}
Пример #11
0
// parent_mat is armature-space
void ArmatureExporter::add_bone_node(Bone *bone,
                                     Object *ob_arm,
                                     SceneExporter *se,
                                     std::vector<Object *> &child_objects)
{
  if (can_export(bone)) {
    std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name);
    std::string node_name = std::string(bone->name);
    std::string node_sid = get_joint_sid(bone);

    COLLADASW::Node node(mSW);

    node.setType(COLLADASW::Node::JOINT);
    node.setNodeId(node_id);
    node.setNodeName(node_name);
    node.setNodeSid(node_sid);

    if (this->export_settings.get_use_blender_profile()) {
      if (!is_export_root(bone)) {
        if (bone->flag & BONE_CONNECTED) {
          node.addExtraTechniqueParameter("blender", "connect", true);
        }
      }
      std::string layers = BoneExtended::get_bone_layers(bone->layer);
      node.addExtraTechniqueParameter("blender", "layer", layers);

      bArmature *armature = (bArmature *)ob_arm->data;
      EditBone *ebone = bc_get_edit_bone(armature, bone->name);
      if (ebone && ebone->roll != 0) {
        node.addExtraTechniqueParameter("blender", "roll", ebone->roll);
      }
      if (bc_is_leaf_bone(bone)) {
        Vector head, tail;
        const BCMatrix &global_transform = this->export_settings.get_global_transform();
        if (this->export_settings.get_apply_global_orientation()) {
          bc_add_global_transform(head, bone->arm_head, global_transform);
          bc_add_global_transform(tail, bone->arm_tail, global_transform);
        }
        else {
          copy_v3_v3(head, bone->arm_head);
          copy_v3_v3(tail, bone->arm_tail);
        }
        node.addExtraTechniqueParameter("blender", "tip_x", tail[0] - head[0]);
        node.addExtraTechniqueParameter("blender", "tip_y", tail[1] - head[1]);
        node.addExtraTechniqueParameter("blender", "tip_z", tail[2] - head[2]);
      }
    }

    node.start();

    add_bone_transform(ob_arm, bone, node);

    // Write nodes of childobjects, remove written objects from list
    std::vector<Object *>::iterator iter = child_objects.begin();

    while (iter != child_objects.end()) {
      Object *ob = *iter;
      if (ob->partype == PARBONE && STREQ(ob->parsubstr, bone->name)) {
        float backup_parinv[4][4];
        copy_m4_m4(backup_parinv, ob->parentinv);

        // crude, temporary change to parentinv
        // so transform gets exported correctly.

        // Add bone tail- translation... don't know why
        // bone parenting is against the tail of a bone
        // and not it's head, seems arbitrary.
        ob->parentinv[3][1] += bone->length;

        // OPEN_SIM_COMPATIBILITY
        // TODO: when such objects are animated as
        // single matrix the tweak must be applied
        // to the result.
        if (export_settings.get_open_sim()) {
          // tweak objects parentinverse to match compatibility
          float temp[4][4];

          copy_m4_m4(temp, bone->arm_mat);
          temp[3][0] = temp[3][1] = temp[3][2] = 0.0f;

          mul_m4_m4m4(ob->parentinv, temp, ob->parentinv);
        }

        se->writeNode(ob);
        copy_m4_m4(ob->parentinv, backup_parinv);
        iter = child_objects.erase(iter);
      }
      else
        iter++;
    }

    for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) {
      add_bone_node(child, ob_arm, se, child_objects);
    }
    node.end();
  }
  else {
    for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) {
      add_bone_node(child, ob_arm, se, child_objects);
    }
  }
}
std::string AnimationExporter::getAnimationPathId(const FCurve *fcu)
{
	std::string rna_path = std::string(fcu->rna_path);
	return translate_id(rna_path);
}
Пример #13
0
void ImagesExporter::export_UV_Image(Image *image, bool use_copies) 
{
	std::string name(id_name(image));
	std::string translated_name(translate_id(name));
	bool not_yet_exported = find(mImages.begin(), mImages.end(), translated_name) == mImages.end();

	if (not_yet_exported) {

		ImBuf *imbuf       = BKE_image_acquire_ibuf(image, NULL, NULL);
		if (!imbuf) {
			fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->name);
			return;
		}

		bool  is_dirty     = (imbuf->userflags & IB_BITMAPDIRTY) != 0;

		ImageFormatData imageFormat;
		BKE_imbuf_to_image_format(&imageFormat, imbuf);

		short image_source = image->source;
		bool  is_generated = image_source == IMA_SRC_GENERATED;
		bool  is_packed    = image->packedfile != NULL;

		char export_path[FILE_MAX];
		char source_path[FILE_MAX];
		char export_dir[FILE_MAX];
		char export_file[FILE_MAX];

		// Destination folder for exported assets
		BLI_split_dir_part(this->export_settings->filepath, export_dir, sizeof(export_dir));

		if (is_generated || is_dirty || use_copies || is_packed) {

			// make absolute destination path

			BLI_strncpy(export_file, name.c_str(), sizeof(export_file));
			BKE_image_path_ensure_ext_from_imformat(export_file, &imageFormat);

			BLI_join_dirfile(export_path, sizeof(export_path), export_dir, export_file);

			// make dest directory if it doesn't exist
			BLI_make_existing_file(export_path);
		}

		if (is_generated || is_dirty || is_packed) {

			// This image in its current state only exists in Blender memory.
			// So we have to export it. The export will keep the image state intact,
			// so the exported file will not be associated with the image.

			if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) {
				fprintf(stderr, "Collada export: Cannot export image to:\n%s\n", export_path);
				return;
			}
			BLI_strncpy(export_path, export_file, sizeof(export_path));
		}
		else {

			// make absolute source path
			BLI_strncpy(source_path, image->name, sizeof(source_path));
			BLI_path_abs(source_path, G.main->name);
			BLI_cleanup_path(NULL, source_path);

			if (use_copies) {
			
				// This image is already located on the file system.
				// But we want to create copies here.
				// To move images into the same export directory.
				// Note: If an image is already located in the export folder,
				// then skip the copy (as it would result in a file copy error).

				if (BLI_path_cmp(source_path, export_path) != 0) {
					if (BLI_copy(source_path, export_path) != 0) {
						fprintf(stderr, "Collada export: Cannot copy image:\n source:%s\ndest :%s\n", source_path, export_path);
						return;
					}
				}

				BLI_strncpy(export_path, export_file, sizeof(export_path));

			}
			else {

				// Do not make any copies, but use the source path directly as reference
				// to the original image

				BLI_strncpy(export_path, source_path, sizeof(export_path));
			}
		}

		COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(export_path)), translated_name, translated_name); /* set name also to mNameNC. This helps other viewers import files exported from Blender better */
		img.add(mSW);
		fprintf(stdout, "Collada export: Added image: %s\n", export_file);
		mImages.push_back(translated_name);

		BKE_image_release_ibuf(image, imbuf, NULL);
	}
}
Пример #14
0
void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial& bind_material, Object *ob, bool active_uv_only, BC_export_texture_type export_texture_type)
{
	bool all_uv_layers = !active_uv_only;
	COLLADASW::InstanceMaterialList& iml = bind_material.getInstanceMaterialList();

	if (export_texture_type == BC_TEXTURE_TYPE_UV)
	{
		std::set<Image *> uv_images = bc_getUVImages(ob, all_uv_layers);
		std::set<Image *>::iterator uv_images_iter;
		for (uv_images_iter = uv_images.begin();
			uv_images_iter != uv_images.end();
			uv_images_iter++) {
			Image *ima = *uv_images_iter;
			std::string matid(id_name(ima));
			matid = get_material_id_from_id(matid);
			std::ostringstream ostr;
			ostr << matid;
			COLLADASW::InstanceMaterial im(ostr.str(), COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));

			// create <bind_vertex_input> for each uv map
			Mesh *me = (Mesh *)ob->data;
			int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);

			int map_index = 0;
			int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE) - 1;
			for (int b = 0; b < totlayer; b++) {
				if (!active_uv_only || b == active_uv_index) {
					char *name = bc_CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
					im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", map_index++));
				}
			}

			iml.push_back(im);
		}
	}

	else {
		for (int a = 0; a < ob->totcol; a++) {
			Material *ma = give_current_material(ob, a + 1);
			if (ma) {
				std::string matid(get_material_id(ma));
				matid = translate_id(matid);
				std::ostringstream ostr;
				ostr << matid;
				COLLADASW::InstanceMaterial im(ostr.str(), COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));

				// create <bind_vertex_input> for each uv map
				Mesh *me = (Mesh *)ob->data;
				int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);

				int map_index = 0;
				int active_uv_index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE) - 1;
				for (int b = 0; b < totlayer; b++) {
					if (!active_uv_only || b == active_uv_index) {
						char *name = bc_CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
						im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", map_index++));
					}
				}

				iml.push_back(im);
			}
		}
	}
}
std::string get_geometry_id(Object *ob, bool use_instantiation)
{
	std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob);

	return translate_id(geom_name) + "-mesh";
}
std::string get_light_id(Object *ob)
{
	return translate_id(id_name(ob)) + "-light";
}
Пример #17
0
// powerful because it handles both cases when there is material and when there's not
void GeometryExporter::createPolylist(short 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) {
		std::ostringstream ostr;
		ostr << translate_id(id_name(ma)) << material_index+1;
		polylist.setMaterial(ostr.str());
	}
			
	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 map 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();
}
std::string get_camera_id(Object *ob)
{
	return translate_id(id_name(ob)) + "-camera";
}
Пример #19
0
//convert f-curves to animation curves and write
void AnimationExporter::dae_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
{
	const char *axis_name = NULL;
	char anim_id[200];

	bool has_tangents = false;
	bool quatRotation = false;

	if (!strcmp(transformName, "rotation_quaternion") ) {
		fprintf(stderr, "quaternion rotation curves are not supported. rotation curve will not be exported\n");
		quatRotation = true;
		return;
	}

	//axis names for colors
	else if (!strcmp(transformName, "color") || !strcmp(transformName, "specular_color") || !strcmp(transformName, "diffuse_color") ||
	         (!strcmp(transformName, "alpha")))
	{
		const char *axis_names[] = {"R", "G", "B"};
		if (fcu->array_index < 3)
			axis_name = axis_names[fcu->array_index];
	}

	//axis names for transforms
	else if ((!strcmp(transformName, "location") || !strcmp(transformName, "scale")) ||
	         (!strcmp(transformName, "rotation_euler")) || (!strcmp(transformName, "rotation_quaternion")))
	{
		const char *axis_names[] = {"X", "Y", "Z"};
		if (fcu->array_index < 3)
			axis_name = axis_names[fcu->array_index];
	}
	else {
		/* no axis name. single parameter */
		axis_name = "";
	}

	std::string ob_name = std::string("null");

	//Create anim Id
	if (ob->type == OB_ARMATURE) {
		ob_name =  getObjectBoneName(ob, fcu);
		BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s.%s", (char *)translate_id(ob_name).c_str(),
		             transformName, axis_name);
	}
	else {
		if (ma)
			ob_name = id_name(ob) + "_material";
		else
			ob_name = id_name(ob);
		BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(),
		             fcu->rna_path, axis_name);
	}

	openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING);

	// create input source
	std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name);

	// create output source
	std::string output_id;

	//quat rotations are skipped for now, because of complications with determining axis.
	if (quatRotation) {
		float *eul  = get_eul_source_for_quat(ob);
		float *eul_axis = (float *)MEM_callocN(sizeof(float) * fcu->totvert, "quat output source values");
		for (int i = 0; i < fcu->totvert; i++) {
			eul_axis[i] = eul[i * 3 + fcu->array_index];
		}
		output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, eul_axis, fcu->totvert, quatRotation, anim_id, axis_name);
		MEM_freeN(eul);
		MEM_freeN(eul_axis);
	}
	else {
		output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name);
	}
	// create interpolations source
	std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name, &has_tangents);

	// handle tangents (if required)
	std::string intangent_id;
	std::string outtangent_id;

	if (has_tangents) {
		// create in_tangent source
		intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name);

		// create out_tangent source
		outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name);
	}

	std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
	COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
	std::string empty;
	sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id));
	sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id));

	// this input is required
	sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));

	if (has_tangents) {
		sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT, COLLADABU::URI(empty, intangent_id));
		sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT, COLLADABU::URI(empty, outtangent_id));
	}

	addSampler(sampler);

	std::string target;

	if (!is_param)
		target = translate_id(ob_name) +
		         "/" + get_transform_sid(fcu->rna_path, -1, axis_name, true);
	else {
		if (ob->type == OB_LAMP)
			target = get_light_id(ob) +
			         "/" + get_light_param_sid(fcu->rna_path, -1, axis_name, true);

		if (ob->type == OB_CAMERA)
			target = get_camera_id(ob) +
			         "/" + get_camera_param_sid(fcu->rna_path, -1, axis_name, true);

		if (ma)
			target = translate_id(id_name(ma)) + "-effect" +
			         "/common/" /*profile common is only supported */ + get_transform_sid(fcu->rna_path, -1, axis_name, true);
	}
	addChannel(COLLADABU::URI(empty, sampler_id), target);

	closeAnimation();
}
std::string get_morph_id(Object *ob)
{
	return translate_id(id_name(ob)) + "-morph";
}
Пример #21
0
void EffectsExporter::operator()(Material *ma, Object *ob)
{
	// create a list of indices to textures of type TEX_IMAGE
	std::vector<int> tex_indices;
	if (this->export_settings->include_material_textures)
		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 > 0.0f) {
		if (ma->spec_shader == MA_SPEC_BLINN) {
			writeBlinn(ep, ma);
		}
		else {
			// \todo figure out handling of all spec+diff shader combos blender has, for now write phong
			// for now set phong in case spec shader is not blinn
			writePhong(ep, ma);
		}
	}
	else {
		if (ma->diff_shader == MA_DIFF_LAMBERT) {
			writeLambert(ep, ma);
		}
		else {
			// \todo figure out handling of all spec+diff shader combos blender has, for now write phong
			writePhong(ep, ma);
		}
	}
	
	// index of refraction
	if (ma->mode & MA_RAYTRANSP) {
		ep.setIndexOfRefraction(ma->ang, false, "index_of_refraction");
	}
	else {
		ep.setIndexOfRefraction(1.0f, false, "index_of_refraction");
	}

	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, false, "transparency");
		// 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, false, "emission");

	// diffuse multiplied by diffuse intensity
	cot = getcol(ma->r * ma->ref, ma->g * ma->ref, ma->b * ma->ref, 1.0f);
	ep.setDiffuse(cot, false, "diffuse");

	// ambient
	/* ma->ambX is calculated only on render, so lets do it here manually and not rely on ma->ambX. */
	if (this->scene->world)
		cot = getcol(this->scene->world->ambr * ma->amb, this->scene->world->ambg * ma->amb, this->scene->world->ambb * ma->amb, 1.0f);
	else
		cot = getcol(ma->amb, ma->amb, ma->amb, 1.0f);

	ep.setAmbient(cot, false, "ambient");

	// 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, false, "specular");
	}

	// 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++;
		}
	}


	std::set<Image *> uv_textures;
	if (ob->type == OB_MESH && ob->totcol && this->export_settings->include_uv_textures) {
		bool active_uv_only = this->export_settings->active_uv_only;
		Mesh *me     = (Mesh *) ob->data;
		int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);

		BKE_mesh_tessface_ensure(me);
		for (int i = 0; i < me->pdata.totlayer; i++) {
			if (!active_uv_only || active_uv_layer == i)
			{
				if (me->pdata.layers[i].type == CD_MTEXPOLY) {
					MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
					MFace *mface = me->mface;
					for (int j = 0; j < me->totpoly; j++, mface++, txface++) {

						Material *mat = give_current_material(ob, mface->mat_nr + 1);
						if (mat != ma) 
							continue;

						Image *ima = txface->tpage;
						if (ima == NULL)
							continue;


						bool not_in_list = uv_textures.find(ima)==uv_textures.end();
						if (not_in_list) {
							std::string name = id_name(ima);
							std::string key(name);
							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> <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);
								samplers[a] = sampler;
								samp_surf[b][0] = &samplers[a];
								im_samp_map[key] = b;
								b++;
								a++;
								uv_textures.insert(ima);
							}
						}
					}
				}
			}
		}
	}

	// 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;

		std::string key(id_name(ima));
		key = translate_id(key);
		int i = im_samp_map[key];
		std::string uvname = strlen(t->uvname) ? t->uvname : active_uv;
		COLLADASW::Sampler *sampler = (COLLADASW::Sampler *)samp_surf[i][0];
		writeTextures(ep, key, sampler, t, ima, uvname);
	}

	std::set<Image *>::iterator uv_t_iter;
	for (uv_t_iter = uv_textures.begin(); uv_t_iter != uv_textures.end(); uv_t_iter++ ) {
		Image *ima = *uv_t_iter;
		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];
		ep.setDiffuse(createTexture(ima, active_uv, sampler), false, "diffuse");
	}

	// 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", "double_sided", 1);
	ep.addExtraTechniques(mSW);

	ep.closeProfile();
	if (twoSided)
		mSW->appendTextBlock("<extra><technique profile=\"MAX3D\"><double_sided>1</double_sided></technique></extra>");
	closeEffect();
}
Пример #22
0
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;
}
Пример #23
0
void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
{
	std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name);
	std::vector<Normal> nor;
	std::vector<BCPolygonNormalsIndices> norind;
	
	if (exportedGeometry.find(geom_id) != exportedGeometry.end())
	{
		return;
	}

	std::string geom_name = kb->name;

	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, geom_name);
	
	// 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();

	//createLooseEdgeList(ob, me, geom_id, norind);

	// XXX slow		
	if (ob->totcol) {
		for (int a = 0; a < ob->totcol; a++) {
			createPolylist(a, has_uvs, has_color, ob, me, geom_id, norind);
		}
	}
	else {
		createPolylist(0, has_uvs, has_color, ob, me, geom_id, norind);
	}
	
	closeMesh();
	
	if (me->flag & ME_TWOSIDED) {
		mSW->appendTextBlock("<extra><technique profile=\"MAYA\"><double_sided>1</double_sided></technique></extra>");
	}

	closeGeometry();
}