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