package org.papervision3d.core.io.exporters { import flash.utils.Dictionary; import org.papervision3d.core.geom.TriangleMesh3D; import org.papervision3d.core.geom.renderables.Triangle3D; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.math.Matrix3D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; /** * This class lets you export a DisplayObject3D to the Collada file format (*.dae). *

* * @author Tim Knip */ public class student_ar_org_papervision3d_core_io_exporters_ExportCollada { public static var DEFAULT_TEXTURE_DIR:String = "."; /** Default visuals scene id and name. */ public static var VISUAL_SCENE_NAME:String = "PapervisionScene"; /** Number of fraction digits to use for floats. */ public static var FRACTION_DIGITS:int = 5; /** Boolean indicatin whether to flip faces. */ public static var REVERSE_WINDING:Boolean = true; /** * */ public static function export(object:DisplayObject3D):String { _hasImages = false; _numImages = 0; _materialToImageId = new Dictionary(true); _numInstances = 0; _numMaterials = 0; _materialTargets = new Dictionary(true); prepareMaterials(object); var xml:String = printLine(''); xml += printLine(''); // export main asset element xml += printLine('', 1); xml += printLine('', 2); xml += printLine('Tim Knip', 3); xml += printLine('Papervision3D - ColladaExport', 3); xml += printLine('', 3); xml += printLine('', 3); xml += printLine('', 2); xml += printLine('2008-05-07T00:05:39Z', 2); xml += printLine('2008-05-07T00:05:39Z', 2); xml += printLine('', 2); xml += printLine('Y_UP', 2); xml += printLine('', 1); // export textures if needed if(_hasImages) { xml += printLine('', 1); xml += exportImages(object, 2); xml += printLine('', 1); } // export materials xml += printLine('', 1); xml += exportMaterials(object, 2); xml += printLine('', 1); // export effects xml += printLine('', 1); xml += exportEffects(object, 2); xml += printLine('', 1); // export geometries xml += printLine('', 1); xml += exportGeometries(object, 2); xml += printLine('', 1); // export scenegraph xml += printLine('', 1); xml += printLine('', 2); xml += exportVisualScene(object, 3); xml += printLine('', 2); xml += printLine('', 1); // export a default collada-scene xml += printLine('', 1); xml += printLine('', 2); xml += printLine('', 1); xml += printLine(''); return xml; } /** * Exports the element. * * @param material * @param indent * * @return XML string */ private static function exportColor(material:MaterialObject3D=null, indent:int=0):String { var rgba:Array = new Array(); if(material) { var color:uint = material is WireframeMaterial ? material.lineColor : material.fillColor; var r:Number = ((color >> 16) & 0xff) / 0xff; var g:Number = ((color >> 8) & 0xff) / 0xff; var b:Number = (color & 0xff) / 0xff; rgba.push(r.toFixed(FRACTION_DIGITS), g.toFixed(FRACTION_DIGITS), b.toFixed(FRACTION_DIGITS), 1.0); } else { rgba.push(0.0, 0.0, 0.0, 1.0); } return printLine('' + rgba.join(" ") + '', indent); } /** * Exports the elements. * * @param object * @param indent * * @return XML string */ private static function exportEffects(object:DisplayObject3D, indent:int=0):String { var xml:String = ""; for(var name:String in object.materials.materialsByName) { var material:MaterialObject3D = object.materials.materialsByName[name]; var tgt:String = _materialTargets[ material ]; var textureId:String = _materialToImageId[material]; xml += printLine('', indent); xml += printLine('', indent+1); if(textureId) { xml += printLine('', indent+2); xml += printLine('', indent+3); xml += printLine(''+textureId+'', indent+4); xml += printLine('A8R8G8B8', indent+4); xml += printLine('', indent+3); xml += printLine('', indent+2); xml += printLine('', indent+2); xml += printLine('', indent+3); xml += printLine(''+textureId+'-surface', indent+4); xml += printLine('LINEAR_MIPMAP_LINEAR', indent+4); xml += printLine('LINEAR', indent+4); xml += printLine('', indent+3); xml += printLine('', indent+2); } xml += printLine('', indent+2); xml += printLine('', indent+3); xml += printLine('', indent+4); xml += exportColor(null, indent + 5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += exportColor(null, indent + 5); xml += printLine('', indent+4); xml += printLine('', indent+4); if(textureId) { xml += printLine('', indent+5); xml += printLine('', indent+5); } else xml += exportColor(material, indent + 5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += exportColor(null, indent + 5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += printLine('20.0', indent+5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += printLine('20.0', indent+5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += printLine('1 1 1 1', indent+5); xml += printLine('', indent+4); xml += printLine('', indent+4); xml += printLine('1.0', indent+5); xml += printLine('', indent+4); xml += printLine('', indent+3); xml += printLine('', indent+2); xml += printLine('', indent+1); xml += printLine('', indent); } for each(var child:DisplayObject3D in object.children) xml += exportEffects(child, indent); return xml; } /** * Exports a element with float data. * * @param id * @param values * @param params * @param indent * * @return XML String */ private static function exportFloatSource(id:String, values:Array, params:Array, indent:int = 0):String { var xml:String = printLine('', indent); var fid:String = id + "-array"; var cnt:int = values.length; var data:String = values.join(" "); var i:int; var line:String = '' + data + ''; xml += printLine(line, indent + 1); xml += printLine('', indent + 1); var stride:int = params.length; cnt = cnt / stride; xml += printLine('', indent + 2); for(i = 0; i < params.length; i++) xml += printLine('', indent + 3); xml += printLine('', indent + 2); xml += printLine('', indent + 1); xml += printLine('', indent); return xml; } /** * Export all geometries and child-geometries for a specific DisplayObject3D * * @param instance * @param indent * * @return XML string */ private static function exportGeometries(instance:DisplayObject3D, indent:int=0):String { var xml:String = ""; if(instance is TriangleMesh3D) xml += exportGeometry(instance as TriangleMesh3D, getInstanceName(instance)+"-geometry", indent); for each(var child:DisplayObject3D in instance.children) xml += exportGeometries(child, indent); return xml; } /** * Exports a mesh's geometry as a Collada element. * * @param mesh * @param id * @param indent * * @return XML string */ private static function exportGeometry(mesh:TriangleMesh3D, id:String, indent:int=0):String { var xml:String = printLine('', indent); var tri:Triangle3D; var v:Vertex3D; var trianglesByMaterial:Object = new Object(); var uvs:Array = new Array(); var vData:Array = new Array(); var uvData:Array = new Array(); var materialName:String; var i:int; var vindices:Dictionary = new Dictionary(true); var uvindices:Dictionary = new Dictionary(true); xml += printLine('', indent + 1); for(i = 0; i < mesh.geometry.vertices.length; i++) { v = mesh.geometry.vertices[i]; vindices[v] = i; vData.push(v.x.toFixed(FRACTION_DIGITS)); vData.push(v.y.toFixed(FRACTION_DIGITS)); vData.push(v.z.toFixed(FRACTION_DIGITS)); } for(i = 0; i < mesh.geometry.faces.length; i++) { tri = mesh.geometry.faces[i]; materialName = findMaterialName(tri.material, mesh.materials); if(!(trianglesByMaterial[materialName] is Array)) trianglesByMaterial[materialName] = new Array(); trianglesByMaterial[materialName].push(i); var idx:int = uvs.length; uvindices[ tri ] = [idx, idx+1, idx+2]; uvs.push(tri.uv0, tri.uv1, tri.uv2); } // build uv source-data for(i = 0; i < uvs.length; i++) uvData.push(uvs[i].u.toFixed(FRACTION_DIGITS), uvs[i].v.toFixed(FRACTION_DIGITS)); // export elements for vertices and uvs xml += exportFloatSource(id+"-positions", vData, ["X", "Y", "Z"], indent + 2); xml += exportFloatSource(id+"-texcoords", uvData, ["S", "T"], indent + 2); // export element xml += printLine('', indent + 2); xml += printLine('', indent + 3); xml += printLine('', indent + 2); // export a element for each material used by the geometry for(materialName in trianglesByMaterial) { var tris:Array = trianglesByMaterial[materialName]; var p:Array = new Array(); for(i = 0; i < tris.length; i++) { tri = mesh.geometry.faces[ tris[i] ]; var uva:Array = uvindices[ tri ]; if(REVERSE_WINDING) { p.push(vindices[tri.v2], uva[2]); p.push(vindices[tri.v1], uva[1]); p.push(vindices[tri.v0], uva[0]); } else { p.push(vindices[tri.v0], uva[0]); p.push(vindices[tri.v1], uva[1]); p.push(vindices[tri.v2], uva[2]); } } xml += printLine('', indent + 2); xml += printLine('', indent + 3); xml += printLine('', indent + 3); xml += printLine('

'+p.join(" ")+'

', indent + 3); xml += printLine('
', indent + 2); } // all done xml += printLine('
', indent + 1); xml += printLine('
', indent); return xml; } /** * Exports the elements. * * @param object * @param indent * * @return XML string */ private static function exportImages(object:DisplayObject3D, indent:int=0):String { var xml:String = ""; for(var name:String in object.materials.materialsByName) { var material:MaterialObject3D = object.materials.materialsByName[name]; if(material is BitmapFileMaterial) { var id:String = "psdFileTex" + (_numImages++); var url:String = BitmapFileMaterial(material).url; url = url.split("\\").join("/"); if(url.indexOf("/") != -1) { var parts:Array = url.split("/"); url = String(parts.pop()); } url = DEFAULT_TEXTURE_DIR + "/" + url; url = url.replace(/\/\//, "/"); xml += printLine('', indent); xml += printLine(''+url+'', indent+1); xml += printLine('', indent); _materialToImageId[ material ] = id; } } for each(var child:DisplayObject3D in object.children) xml += exportImages(child, indent); return xml; } /** * Exports the elements. * * @param object * @param indent * * @return XML string */ private static function exportMaterials(object:DisplayObject3D, indent:int=0):String { var xml:String = ""; for(var name:String in object.materials.materialsByName) { var material:MaterialObject3D = object.materials.materialsByName[name]; var tgt:String = _materialTargets[ material ]; xml += printLine('', indent); xml += printLine('', indent + 1); xml += printLine('', indent); } for each(var child:DisplayObject3D in object.children) xml += exportMaterials(child, indent); return xml; } /** * Exports a element. * * @param matrix * @param indent * * @return XML string */ private static function exportMatrix(matrix:Matrix3D, indent:int=0):String { var data:Array = [ matrix.n11, matrix.n12, matrix.n13, matrix.n14, matrix.n21, matrix.n22, matrix.n23, matrix.n24, matrix.n31, matrix.n32, matrix.n33, matrix.n34, matrix.n41, matrix.n42, matrix.n43, matrix.n44 ]; return printLine('' + data.join(" ") + '', indent); } /** * Exports a element. * * @param object * @param indent * * @return XML string */ private static function exportVisualScene(object:DisplayObject3D, indent:int=0):String { var id:String = getInstanceName(object); var xml:String = printLine('', indent); xml += exportMatrix(object.transform, indent + 1); if(object is TriangleMesh3D) { xml += printLine('', indent + 1); xml += printLine('', indent + 2); xml += printLine('', indent + 3); for(var materialName:String in object.materials.materialsByName) { var material:MaterialObject3D = object.materials.materialsByName[materialName]; var tgt:String = _materialTargets[material]; xml += printLine('', indent + 4); } xml += printLine('', indent + 3); xml += printLine('', indent + 2); xml += printLine('', indent + 1); } for each(var child:DisplayObject3D in object.children) xml += exportVisualScene(child, indent + 1); xml += printLine('', indent); return xml; } /** * */ private static function getInstanceName(instance:DisplayObject3D):String { if(instance.name && instance.name.length > 2) return instance.name; instance.name = "Node" + (_numInstances++); return instance.name; } /** * */ private static function prepareMaterials(object:DisplayObject3D):void { object.materials = object.materials || new MaterialsList(); if(object.geometry && object.geometry.faces) { for each(var triangle:Triangle3D in object.geometry.faces) { var name:String = findMaterialName(triangle.material, object.materials); if(!name) { name = "Material" + _numMaterials; object.materials.addMaterial(triangle.material, name); _numMaterials++; } } } for(var materialName:String in object.materials.materialsByName) { var material:MaterialObject3D = object.materials.materialsByName[ materialName ]; if(material is BitmapFileMaterial) _hasImages = true; _materialTargets[material] = materialName.toLowerCase() + "-target"; } for each(var child:DisplayObject3D in object.children) prepareMaterials(child); } /** * */ private static function findMaterialName(find:MaterialObject3D, list:MaterialsList):String { for(var name:String in list.materialsByName) { if(list.materialsByName[name] === find) return name; } return null; } private static function printLine(str:String, indent:int = 0):String { var s:String = ""; for(var i:int = 0; i < indent; i++) s += "\t"; return s + str + "\n"; } private static var _numInstances : int = 0; private static var _numMaterials : int = 0; private static var _materialTargets : Dictionary; private static var _hasImages : Boolean; private static var _numImages : int = 0; private static var _materialToImageId : Dictionary; } }