package away3d.loaders
{
import away3d.animators.*;
import away3d.animators.skin.*;
import away3d.arcane;
import away3d.containers.*;
import away3d.core.base.*;
import away3d.core.math.*;
import away3d.core.utils.*;
import away3d.loaders.data.*;
import away3d.loaders.utils.*;
import away3d.materials.*;
import flash.utils.*;
use namespace arcane;
/**
* File loader for the Collada file format with animation.
*/
public class Collada extends AbstractParser
{
/** @private */
arcane var ini:Init;
private var collada:XML;
private var material:ITriangleMaterial;
private var centerMeshes:Boolean;
private var scaling:Number;
private var shading:Boolean;
private var texturePath:String;
private var autoLoadTextures:Boolean;
private var materialLibrary:MaterialLibrary;
private var animationLibrary:AnimationLibrary;
private var geometryLibrary:GeometryLibrary;
private var channelLibrary:ChannelLibrary;
private var symbolLibrary:Dictionary;
private var yUp:Boolean;
private var toRADIANS:Number = Math.PI / 180;
private var _skinController:SkinController;
private var _meshData:MeshData;
private var _geometryData:GeometryData;
private var _materialData:MaterialData;
private var _animationData:AnimationData;
private var _meshMaterialData:MeshMaterialData;
private var numChildren:int;
private var _maxX:Number;
private var _minX:Number;
private var _maxY:Number;
private var _minY:Number;
private var _maxZ:Number;
private var _minZ:Number;
private var _faceListIndex:int;
private var _faceData:FaceData;
private var _face:Face;
private var _vertex:Vertex;
private var _moveVector:Number3D = new Number3D();
private var rotationMatrix:MatrixAway3D = new MatrixAway3D();
private var scalingMatrix:MatrixAway3D = new MatrixAway3D();
private var translationMatrix:MatrixAway3D = new MatrixAway3D();
private var VALUE_X:String;
private var VALUE_Y:String;
private var VALUE_Z:String;
private var VALUE_U:String = "S";
private var VALUE_V:String = "T";
private var _geometryArray:Array;
private var _geometryArrayLength:int;
private var _channelArray:Array;
private var _channelArrayLength:int;
/**
* Collada Animation
*/
private var _defaultAnimationClip:AnimationData;
private var _haveAnimation:Boolean = false;
private var _haveClips:Boolean = false;
private var _containers:Dictionary = new Dictionary(true);
private function buildContainers(containerData:ContainerData, parent:ObjectContainer3D):void
{
for each (var _objectData:ObjectData in containerData.children) {
if (_objectData is MeshData) {
buildMesh(_objectData as MeshData, parent);
} else if (_objectData is BoneData) {
var _boneData:BoneData = _objectData as BoneData;
var bone:Bone = new Bone({name:_boneData.name});
_boneData.container = bone as ObjectContainer3D;
_containers[bone.name] = bone;
//ColladaMaya 3.05B
bone.id = _boneData.id;
bone.transform = _boneData.transform;
bone.joint.transform = _boneData.jointTransform;
buildContainers(_boneData, bone.joint);
parent.addChild(bone);
} else if (_objectData is ContainerData) {
var _containerData:ContainerData = _objectData as ContainerData;
var objectContainer:ObjectContainer3D = _containerData.container = new ObjectContainer3D({name:_containerData.name});
_containers[objectContainer.name] = objectContainer;
objectContainer.transform = _objectData.transform;
buildContainers(_containerData, objectContainer);
if (centerMeshes && objectContainer.children.length) {
//center children in container for better bounding radius calulations
objectContainer.movePivot(_moveVector.x = (objectContainer.maxX + objectContainer.minX)/2, _moveVector.y = (objectContainer.maxY + objectContainer.minY)/2, _moveVector.z = (objectContainer.maxZ + objectContainer.minZ)/2);
_moveVector.transform(_moveVector, _objectData.transform);
objectContainer.moveTo(_moveVector.x, _moveVector.y, _moveVector.z);
}
parent.addChild(objectContainer);
}
}
}
private function buildMesh(_meshData:MeshData, parent:ObjectContainer3D):void
{
Debug.trace(" + Build Mesh : "+_meshData.name)
var mesh:Mesh = new Mesh({name:_meshData.name});
mesh.transform = _meshData.transform;
mesh.bothsides = _meshData.geometry.bothsides;
_geometryData = _meshData.geometry;
var geometry:Geometry = _geometryData.geometry;
if (!geometry) {
geometry = _geometryData.geometry = new Geometry();
mesh.geometry = geometry;
//set materialdata for each face
for each (_meshMaterialData in _geometryData.materials) {
for each (_faceListIndex in _meshMaterialData.faceList) {
_faceData = _geometryData.faces[_faceListIndex] as FaceData;
_faceData.materialData = symbolLibrary[_meshMaterialData.symbol];
}
}
if (_geometryData.skinVertices.length) {
var i:int;
var joints:Array;
var rootBone:Bone = (container as ObjectContainer3D).getBoneByName(_meshData.skeleton);
geometry.skinVertices = _geometryData.skinVertices;
geometry.skinControllers = _geometryData.skinControllers;
//mesh.bone = container.getChildByName(_meshData.bone) as Bone;
geometry.rootBone = rootBone;
for each (_skinController in geometry.skinControllers)
_skinController.inverseTransform = parent.inverseSceneTransform;
}
//create faces from face and mesh data
var face:Face;
var matData:MaterialData;
for each(_faceData in _geometryData.faces) {
if (!_faceData.materialData)
continue;
_face = new Face(_geometryData.vertices[_faceData.v0],
_geometryData.vertices[_faceData.v1],
_geometryData.vertices[_faceData.v2],
_faceData.materialData.material as ITriangleMaterial,
_geometryData.uvs[_faceData.uv0],
_geometryData.uvs[_faceData.uv1],
_geometryData.uvs[_faceData.uv2]);
geometry.addFace(_face);
_faceData.materialData.elements.push(_face);
}
} else {
mesh.geometry = geometry;
}
if (centerMeshes) {
trace(_geometryData.maxX);
trace(_geometryData.minX);
mesh.movePivot(_moveVector.x = (_geometryData.maxX + _geometryData.minX)/2, _moveVector.y = (_geometryData.maxY + _geometryData.minY)/2, _moveVector.z = (_geometryData.maxZ + _geometryData.minZ)/2);
_moveVector.transform(_moveVector, _meshData.transform);
mesh.moveTo(_moveVector.x, _moveVector.y, _moveVector.z);
}
mesh.type = ".Collada";
parent.addChild(mesh);
}
private function buildMaterials():void
{
for each (_materialData in materialLibrary)
{
Debug.trace(" + Build Material : "+_materialData.name)
//overridden by the material property in constructor
if (material)
_materialData.material = material;
//overridden by materials passed in contructor
if (_materialData.material)
continue;
switch (_materialData.materialType)
{
case MaterialData.TEXTURE_MATERIAL:
materialLibrary.loadRequired = true;
break;
case MaterialData.SHADING_MATERIAL:
_materialData.material = new ShadingColorMaterial(null, {ambient:_materialData.ambientColor, diffuse:_materialData.diffuseColor, specular:_materialData.specularColor, shininess:_materialData.shininess});
break;
case MaterialData.COLOR_MATERIAL:
_materialData.material = new ColorMaterial(_materialData.diffuseColor);
break;
case MaterialData.WIREFRAME_MATERIAL:
_materialData.material = new WireColorMaterial();
break;
}
}
}
private function buildAnimations():void
{
var bone:Bone;
for each (_geometryData in geometryLibrary) {
for each (_skinController in _geometryData.geometry.skinControllers) {
bone = (container as ObjectContainer3D).getBoneByName(_skinController.name);
if (bone)
_skinController.joint = bone.joint;
else
Debug.warning("no joint found for " + _skinController.name);
}
}
for each (_animationData in animationLibrary)
{
switch (_animationData.animationType)
{
case AnimationData.SKIN_ANIMATION:
var animation:SkinAnimation = new SkinAnimation();
var param:Array;
var rX:String;
var rY:String;
var rZ:String;
var sX:String;
var sY:String;
var sZ:String;
for each (var channelData:ChannelData in _animationData.channels) {
var channel:Channel = channelData.channel;
channel.target = _containers[channel.name];
animation.appendChannel(channel);
var times:Array = channel.times;
if (_animationData.start > times[0])
_animationData.start = times[0];
if (_animationData.end < times[times.length-1])
_animationData.end = times[times.length - 1];
if (channel.target is Bone) {
rX = "jointRotationX";
rY = "jointRotationY";
rZ = "jointRotationZ";
sX = "jointScaleX";
sY = "jointScaleY";
sZ = "jointScaleZ";
} else {
rX = "rotationX";
rY = "rotationY";
rZ = "rotationZ";
sX = "scaleX";
sY = "scaleY";
sZ = "scaleZ";
}
switch(channelData.type)
{
case "translateX":
channel.type = ["x"];
if (yUp)
for each (param in channel.param)
param[0] *= -1*scaling;
break;
case "translateY":
if (yUp)
channel.type = ["y"];
else
channel.type = ["z"];
for each (param in channel.param)
param[0] *= scaling;
break;
case "translateZ":
if (yUp)
channel.type = ["z"];
else
channel.type = ["y"];
for each (param in channel.param)
param[0] *= scaling;
break;
case "jointOrientX":
channel.type = ["rotationX"];
if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "rotateX":
case "RotX":
channel.type = [rX];
if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "jointOrientY":
channel.type = ["rotationY"];
//if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "rotateY":
case "RotY":
if (yUp)
channel.type = [rY];
else
channel.type = [rZ];
//if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "jointOrientZ":
channel.type = ["rotationZ"];
//if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "rotateZ":
case "RotZ":
if (yUp)
channel.type = [rZ];
else
channel.type = [rY];
//if (yUp)
for each (param in channel.param)
param[0] *= -1;
break;
case "scaleX":
channel.type = [sX];
//if (yUp)
// for each (param in channel.param)
// param[0] *= -1;
break;
case "scaleY":
if (yUp)
channel.type = [sY];
else
channel.type = [sZ];
break;
case "scaleZ":
if (yUp)
channel.type = [sZ];
else
channel.type = [sY];
break;
case "translate":
case "translation":
if (yUp) {
channel.type = ["x", "y", "z"];
for each (param in channel.param)
param[0] *= -1;
} else {
channel.type = ["x", "z", "y"];
}
for each (param in channel.param) {
param[0] *= scaling;
param[1] *= scaling;
param[2] *= scaling;
}
break;
case "scale":
if (yUp)
channel.type = [sX, sY, sZ];
else
channel.type = [sX, sZ, sY];
break;
case "rotate":
if (yUp) {
channel.type = [rX, rY, rZ];
for each (param in channel.param) {
param[0] *= -1;
param[1] *= -1;
param[2] *= -1;
}
} else {
channel.type = [rX, rZ, rY];
for each (param in channel.param) {
param[1] *= -1;
param[2] *= -1;
}
}
break;
case "transform":
channel.type = ["transform"];
break;
}
}
animation.start = _animationData.start;
animation.length = _animationData.end - _animationData.start;
_animationData.animation = animation;
break;
case AnimationData.VERTEX_ANIMATION:
break;
}
}
}
private function getArray(spaced:String):Array
{
spaced = spaced.split("\r\n").join(" ");
var strings:Array = spaced.split(" ");
var numbers:Array = [];
var totalStrings:Number = strings.length;
for (var i:Number = 0; i < totalStrings; i++)
if (strings[i] != "")
numbers.push(Number(strings[i]));
return numbers;
}
private function rotateMatrix(vector:Array):MatrixAway3D
{
if (yUp) {
rotationMatrix.rotationMatrix(vector[0], -vector[1], -vector[2], vector[3]*toRADIANS);
} else {
rotationMatrix.rotationMatrix(vector[0], vector[2], vector[1], -vector[3]*toRADIANS);
}
return rotationMatrix;
}
private function translateMatrix(vector:Array):MatrixAway3D
{
if (yUp)
translationMatrix.translationMatrix(-vector[0]*scaling, vector[1]*scaling, vector[2]*scaling);
else
translationMatrix.translationMatrix(vector[0]*scaling, vector[2]*scaling, vector[1]*scaling);
return translationMatrix;
}
private function scaleMatrix(vector:Array):MatrixAway3D
{
if (yUp)
scalingMatrix.scaleMatrix(vector[0], vector[1], vector[2]);
else
scalingMatrix.scaleMatrix(vector[0], vector[2], vector[1]);
return scalingMatrix;
}
private function getId(url:String):String
{
return url.split("#")[1];
}
/**
* Container data object used for storing the parsed collada data structure.
*/
public var containerData:ContainerData;
/**
* Creates a new Collada object. Not intended for direct use, use the static parse or load methods.
*
* @param xml The xml data of a loaded file.
* @param init [optional] An initialisation object for specifying default instance properties.
*
* @see away3d.loaders.Collada#parse()
* @see away3d.loaders.Collada#load()
*/
public function Collada(data:*, init:Object = null)
{
collada = Cast.xml(data);
ini = Init.parse(init);
texturePath = ini.getString("texturePath", "");
autoLoadTextures = ini.getBoolean("autoLoadTextures", true);
scaling = ini.getNumber("scaling", 1)*100;
shading = ini.getBoolean("shading", false);
material = ini.getMaterial("material");
centerMeshes = ini.getBoolean("centerMeshes", false);
var materials:Object = ini.getObject("materials") || {};
//create the container
container = new ObjectContainer3D(ini);
container.name = "collada";
materialLibrary = container.materialLibrary = new MaterialLibrary();
animationLibrary = container.animationLibrary = new AnimationLibrary();
geometryLibrary = container.geometryLibrary = new GeometryLibrary();
channelLibrary = new ChannelLibrary();
symbolLibrary = new Dictionary(true);
materialLibrary.autoLoadTextures = autoLoadTextures;
materialLibrary.texturePath = texturePath;
//organise the materials
for (var name:String in materials) {
_materialData = materialLibrary.addMaterial(name);
_materialData.material = Cast.material(materials[name]);
//determine material type
if (_materialData.material is BitmapMaterial)
_materialData.materialType = MaterialData.TEXTURE_MATERIAL;
else if (_materialData.material is ShadingColorMaterial)
_materialData.materialType = MaterialData.SHADING_MATERIAL;
else if (_materialData.material is WireframeMaterial)
_materialData.materialType = MaterialData.WIREFRAME_MATERIAL;
}
//parse the collada file
parseCollada();
}
/**
* Creates a 3d container object from the raw xml data of a collada file.
*
* @param data The xml data of a loaded file.
* @param init [optional] An initialisation object for specifying default instance properties.
* @param loader [optional] Not intended for direct use.
*
* @return A 3d loader object that can be used as a placeholder in a scene while the file is parsing.
*/
public static function parse(data:*, init:Object = null):ObjectContainer3D
{
return Object3DLoader.parseGeometry(data, Collada, init).handle as ObjectContainer3D;
}
/**
* Loads and parses a collada file into a 3d container object.
*
* @param url The url location of the file to load.
* @param init [optional] An initialisation object for specifying default instance properties.
* @return A 3d loader object that can be used as a placeholder in a scene while the file is loading.
*/
public static function load(url:String, init:Object = null):Object3DLoader
{
//texturePath as model folder
if (url)
{
var _pathArray :Array = url.split("/");
var _imageName :String = _pathArray.pop();
var _texturePath :String = (_pathArray.length>0)?_pathArray.join("/")+"/":_pathArray.join("/");
if (init)
init.texturePath = init.texturePath || _texturePath;
else
init = {texturePath:_texturePath};
}
return Object3DLoader.loadGeometry(url, Collada, false, init);
}
/**
* @inheritDoc
*/
public override function parseNext():void
{
if (_parsedChunks < _geometryArrayLength)
parseGeometry(_geometryArray[_parsedChunks]);
else
parseChannel(_channelArray[-_geometryArrayLength + _parsedChunks]);
_parsedChunks++;
if (_parsedChunks == _totalChunks) {
//build materials
buildMaterials();
//build the containers
buildContainers(containerData, container as ObjectContainer3D);
//build the meshes
//buildMeshes(containerData, container as ObjectContainer3D);
//build animations
buildAnimations();
notifySuccess();
} else {
notifyProgress();
}
}
private function parseCollada():void
{
default xml namespace = collada.namespace();
Debug.trace(" ! ------------- Begin Parse Collada -------------");
// Get up axis
yUp = (collada.asset.up_axis == "Y_UP")||(String(collada.asset.up_axis) == "");
if (yUp) {
VALUE_X = "X";
VALUE_Y = "Y";
VALUE_Z = "Z";
} else {
VALUE_X = "X";
VALUE_Y = "Z";
VALUE_Z = "Y";
}
parseScene();
parseAnimationClips();
}
/**
* Converts the scene heirarchy to an Away3d data structure
*/
private function parseScene():void
{
var scene:XML = collada.library_visual_scenes.visual_scene.(@id == getId(collada.scene.instance_visual_scene.@url))[0];
if (scene == null) {
Debug.trace(" ! ------------- No scene to parse -------------");
return;
}
Debug.trace(" ! ------------- Begin Parse Scene -------------");
containerData = new ContainerData();
for each (var node:XML in scene.node)
parseNode(node, containerData);
Debug.trace(" ! ------------- End Parse Scene -------------");
_geometryArray = geometryLibrary.getGeometryArray();
_geometryArrayLength = _geometryArray.length;
_totalChunks += _geometryArrayLength;
}
/**
* Converts a single scene node to a BoneData ContainerData or MeshData object.
*
* @see away3d.loaders.data.BoneData
* @see away3d.loaders.data.ContainerData
* @see away3d.loaders.data.MeshData
*/
private function parseNode(node:XML, parent:ContainerData):void
{
var _transform:MatrixAway3D;
var _objectData:ObjectData;
var _name:String = node.name().localName;
if (String(node.instance_light.@url) != "" || String(node.instance_camera.@url) != "")
return;
if (String(node.instance_controller) == "" && String(node.instance_geometry) == "")
{
if (String(node.@type) == "JOINT")
_objectData = new BoneData();
else {
if (String(node.instance_node.@url) == "" && (String(node.node) == "" || parent is BoneData))
return;
_objectData = new ContainerData();
}
}else{
_objectData = new MeshData();
}
parent.children.push(_objectData);
//ColladaMaya 3.05B
if (String(node.@type) == "JOINT")
_objectData.id = node.@sid;
else
_objectData.id = node.@id;
//ColladaMaya 3.02
_objectData.name = node.@id;
_transform = _objectData.transform;
Debug.trace(" + Parse Node : " + _objectData.id + " : " + _objectData.name);
var geo:XML;
var ctrlr:XML;
var sid:String;
var instance_material:XML;
var arrayChild:Array
var boneData:BoneData = (_objectData as BoneData);
for each (var child:XML in node.children())
{
arrayChild = getArray(child);
switch (child.name().localName)
{
case "translate":
_transform.multiply(_transform, translateMatrix(arrayChild));
break;
case "rotate":
sid = child.@sid;
if (_objectData is BoneData && (sid == "rotateX" || sid == "rotateY" || sid == "rotateZ" || sid == "rotX" || sid == "rotY" || sid == "rotZ"))
boneData.jointTransform.multiply(boneData.jointTransform, rotateMatrix(arrayChild));
else
_transform.multiply(_transform, rotateMatrix(arrayChild));
break;
case "scale":
if (_objectData is BoneData)
boneData.jointTransform.multiply(boneData.jointTransform, scaleMatrix(arrayChild));
else
_transform.multiply(_transform, scaleMatrix(arrayChild));
break;
// Baked transform matrix
case "matrix":
var m:MatrixAway3D = new MatrixAway3D();
m.array2matrix(arrayChild, yUp, scaling);
_transform.multiply(_transform, m);
break;
case "node":
//3dsMax 11 - Feeling ColladaMax v3.05B
//
if(_objectData is MeshData)
{
parseNode(child, parent as ContainerData);
}else{
parseNode(child, _objectData as ContainerData);
}
break;
case "instance_node":
parseNode(collada.library_nodes.node.(@id == getId(child.@url))[0], _objectData as ContainerData);
break;
case "instance_geometry":
if(String(child).indexOf("lines") == -1) {
//add materials to materialLibrary
for each (instance_material in child..instance_material)
parseMaterial(instance_material.@symbol, getId(instance_material.@target));
geo = collada.library_geometries.geometry.(@id == getId(child.@url))[0];
(_objectData as MeshData).geometry = geometryLibrary.addGeometry(geo.@id, geo);
}
break;
case "instance_controller":
//add materials to materialLibrary
for each (instance_material in child..instance_material)
parseMaterial(instance_material.@symbol, getId(instance_material.@target));
ctrlr = collada.library_controllers.controller.(@id == getId(child.@url))[0];
geo = collada.library_geometries.geometry.(@id == getId(ctrlr.skin[0].@source))[0];
(_objectData as MeshData).geometry = geometryLibrary.addGeometry(geo.@id, geo, ctrlr);
(_objectData as MeshData).skeleton = getId(child.skeleton);
break;
}
}
}
/**
* Converts a material definition to a MaterialData object
*
* @see away3d.loaders.data.MaterialData
*/
private function parseMaterial(symbol:String, name:String):void
{
_materialData = materialLibrary.addMaterial(name);
symbolLibrary[symbol] = _materialData;
if(symbol == "FrontColorNoCulling") {
_materialData.materialType = MaterialData.SHADING_MATERIAL;
} else {
_materialData.textureFileName = getTextureFileName(name);
if (_materialData.textureFileName) {
_materialData.materialType = MaterialData.TEXTURE_MATERIAL;
} else {
if (shading)
_materialData.materialType = MaterialData.SHADING_MATERIAL;
else
_materialData.materialType = MaterialData.COLOR_MATERIAL;
parseColorMaterial(name, _materialData);
}
}
}
/**
* Parses geometry data.
*
* @see away3d.loaders.data.GeometryData
*/
private function parseGeometry(geometryData:GeometryData):void
{
Debug.trace(" + Parse Geometry : "+ geometryData.name);
var verticesDictionary:Dictionary = new Dictionary(true);
// Triangles
for each (var triangles:XML in geometryData.geoXML.mesh.triangles)
{
// Input
var field:Array = [];
for each(var input:XML in triangles.input)
{
var semantic:String = input.@semantic;
switch(semantic)
{
case "VERTEX":
deserialize(input, geometryData.geoXML, Vertex, geometryData.vertices);
break;
case "TEXCOORD":
deserialize(input, geometryData.geoXML, UV, geometryData.uvs);
break;
default:
}
field.push(input.@semantic);
}
var data :Array = triangles.p.split(' ');
var len :Number = triangles.@count;
var symbol :String = triangles.@material
Debug.trace(" + Parse MeshMaterialData");
_meshMaterialData = new MeshMaterialData();
_meshMaterialData.symbol = symbol;
geometryData.materials.push(_meshMaterialData);
//if (!materialLibrary[material])
// parseMaterial(material, material);
for (var j:Number = 0; j < len; j++)
{
var _faceData:FaceData = new FaceData();
for (var vn:Number = 0; vn < 3; vn++)
{
for each (var fld:String in field)
{
switch(fld)
{
case "VERTEX":
_faceData["v" + vn] = data.shift();
break;
case "TEXCOORD":
_faceData["uv" + vn] = data.shift();
break;
default:
data.shift();
}
}
}
//trace(_faceData.v0);
verticesDictionary[_faceData.v0] = geometryData.vertices[_faceData.v0];
verticesDictionary[_faceData.v1] = geometryData.vertices[_faceData.v1];
verticesDictionary[_faceData.v2] = geometryData.vertices[_faceData.v2];
_meshMaterialData.faceList.push(geometryData.faces.length);
geometryData.faces.push(_faceData);
}
}
//center vertex points in mesh for better bounding radius calulations
if (centerMeshes) {
geometryData.maxX = -Infinity;
geometryData.minX = Infinity;
geometryData.maxY = -Infinity;
geometryData.minY = Infinity;
geometryData.maxZ = -Infinity;
geometryData.minZ = Infinity;
for each (_vertex in verticesDictionary) {
if (geometryData.maxX < _vertex._x)
geometryData.maxX = _vertex._x;
if (geometryData.minX > _vertex._x)
geometryData.minX = _vertex._x;
if (geometryData.maxY < _vertex._y)
geometryData.maxY = _vertex._y;
if (geometryData.minY > _vertex._y)
geometryData.minY = _vertex._y;
if (geometryData.maxZ < _vertex._z)
geometryData.maxZ = _vertex._z;
if (geometryData.minZ > _vertex._z)
geometryData.minZ = _vertex._z;
}
}
// Double Side
if (String(geometryData.geoXML.extra.technique.double_sided) != "")
geometryData.bothsides = (geometryData.geoXML.extra.technique.double_sided[0].toString() == "1");
else
geometryData.bothsides = false;
//parse controller
if (!geometryData.ctrlXML)
return;
var skin:XML = geometryData.ctrlXML.skin[0];
var jointId:String = getId(skin.joints.input.(@semantic == "JOINT")[0].@source);
var tmp:String = skin.source.(@id == jointId).Name_array.toString();
//Blender?
if (!tmp) tmp = skin.source.(@id == jointId).IDREF_array.toString();
tmp = tmp.replace(/\n/g, " ");
var nameArray:Array = tmp.split(" ");
var bind_shape:MatrixAway3D = new MatrixAway3D();
bind_shape.array2matrix(getArray(skin.bind_shape_matrix[0].toString()), yUp, scaling);
var bindMatrixId:String = getId(skin.joints.input.(@semantic == "INV_BIND_MATRIX").@source);
var float_array:Array = getArray(skin.source.(@id == bindMatrixId)[0].float_array.toString());
var v:Array;
var matrix:MatrixAway3D;
var name:String;
var joints:Array = new Array();
var skinController:SkinController;
var i:int = 0;
while (i < float_array.length)
{
name = nameArray[i / 16];
matrix = new MatrixAway3D();
matrix.array2matrix(float_array.slice(i, i+16), yUp, scaling);
matrix.multiply(matrix, bind_shape);
geometryData.skinControllers.push(skinController = new SkinController());
skinController.name = name;
skinController.bindMatrix = matrix;
i = i + 16;
}
Debug.trace(" + SkinWeight");
tmp = skin.vertex_weights[0].@count;
var num_weights:int = int(skin.vertex_weights[0].@count);
var weightsId:String = getId(skin.vertex_weights.input.(@semantic == "WEIGHT")[0].@source);
tmp = skin.source.(@id == weightsId).float_array.toString();
var weights:Array = tmp.split(" ");
tmp = skin.vertex_weights.vcount.toString();
var vcount:Array = tmp.split(" ");
tmp = skin.vertex_weights.v.toString();
v = tmp.split(" ");
var skinVertex :SkinVertex;
var c :int;
var count :int = 0;
i=0;
while (i < geometryData.vertices.length)
{
c = int(vcount[i]);
skinVertex = new SkinVertex(geometryData.vertices[i]);
geometryData.skinVertices.push(skinVertex);
j=0;
while (j < c)
{
skinVertex.controllers.push(geometryData.skinControllers[int(v[count])]);
count++;
skinVertex.weights.push(Number(weights[int(v[count])]));
count++;
j++;
}
i++;
}
}
/**
* Detects and parses all animation clips
*/
private function parseAnimationClips() : void
{
//Check for animations
var anims:XML = collada.library_animations[0];
if (!anims) {
Debug.trace(" ! ------------- No animations to parse -------------");
return;
}
//Check to see if animation clips exist
var clips:XML = collada.library_animation_clips[0];
Debug.trace(" ! Animation Clips Exist : " + _haveClips);
Debug.trace(" ! ------------- Begin Parse Animation -------------");
//loop through all animation channels
for each (var channel:XML in anims.animation)
channelLibrary.addChannel(channel.@id, channel);
if (clips) {
//loop through all animation clips
for each (var clip:XML in clips.animation_clip)
parseAnimationClip(clip);
}
//create default animation clip
_defaultAnimationClip = animationLibrary.addAnimation("default");
for each (var channelData:ChannelData in channelLibrary)
_defaultAnimationClip.channels[channelData.name] = channelData;
Debug.trace(" ! ------------- End Parse Animation -------------");
_channelArray = channelLibrary.getChannelArray();
_channelArrayLength = _channelArray.length;
_totalChunks += _channelArrayLength;
}
private function parseAnimationClip(clip:XML) : void
{
var animationClip:AnimationData = animationLibrary.addAnimation(clip.@id);
for each (var channel:XML in clip.instance_animation)
animationClip.channels[getId(channel.@url)] = channelLibrary[getId(channel.@url)];
}
private function parseChannel(channelData:ChannelData) : void
{
var node:XML = channelData.xml;
var id:String = node.channel.@target;
var name:String = id.split("/")[0];
var type:String = id.split("/")[1];
var sampler:XML = node.sampler[0];
if (!type) {
Debug.trace(" ! No animation type detected");
return;
}
type = type.split(".")[0];
if (type == "image" || node.@id.split(".")[1] == "frameExtension")
{
//TODO : Material Animation
Debug.trace(" ! Material animation not yet implemented");
return;
}
var channel:Channel = channelData.channel = new Channel(name);
var i:int;
var j:int;
_defaultAnimationClip.channels[channelData.name] = channelData;
Debug.trace(" ! channelType : " + type);
for each (var input:XML in sampler.input)
{
var src:XML = node.source.(@id == getId(input.@source))[0]
var count:int = int(src.float_array.@count);
var list:Array = String(src.float_array).split(" ");
var len:int = int(src.technique_common.accessor.@count);
var stride:int = int(src.technique_common.accessor.@stride);
var semantic:String = input.@semantic;
var p:String
var sign:int = (type.charAt(type.length - 1) == "X")? -1 : 1;
switch(semantic) {
case "INPUT":
for each (p in list)
channel.times.push(Number(p));
if (_defaultAnimationClip.start > channel.times[0])
_defaultAnimationClip.start = channel.times[0];
if (_defaultAnimationClip.end < channel.times[channel.times.length-1])
_defaultAnimationClip.end = channel.times[channel.times.length-1];
break;
case "OUTPUT":
i=0
while (i < len) {
channel.param[i] = new Array();
if (stride == 16) {
var m:MatrixAway3D = new MatrixAway3D();
m.array2matrix(list.slice(i*stride, i*stride + 16), yUp, scaling)
channel.param[i].push(m);
} else {
j = 0;
while (j < stride) {
channel.param[i].push(Number(list[i*stride + j]));
j++;
}
}
i++;
}
break;
case "INTERPOLATION":
for each (p in list)
{
channel.interpolations.push(p);
}
break;
case "IN_TANGENT":
i=0;
while (i < len)
{
channel.inTangent[i] = new Array();
j = 0;
while (j < stride) {
channel.inTangent[i].push(new Number2D(Number(list[stride * i + j]), Number(list[stride * i + j + 1])));
j++;
}
i++;
}
break;
case "OUT_TANGENT":
i=0;
while (i < len)
{
channel.outTangent[i] = new Array();
j = 0;
while (j < stride) {
channel.outTangent[i].push(new Number2D(Number(list[stride * i + j]), Number(list[stride * i + j + 1])));
j++;
}
i++;
}
break;
}
}
channelData.type = type;
}
/**
* Retrieves the filename of a material
*/
private function getTextureFileName( name:String ):String
{
var filename :String = null;
var material:XML = collada.library_materials.material.(@id == name)[0];
if( material )
{
var effectId:String = getId( material.instance_effect.@url );
var effect:XML = collada.library_effects.effect.(@id == effectId)[0];
if (effect..texture.length() == 0) return null;
var textureId:String = effect..texture[0].@texture;
var sampler:XML = effect..newparam.(@sid == textureId)[0];
// Blender
var imageId:String = textureId;
// Not Blender
if( sampler )
{
var sourceId:String = sampler..source[0];
var source:XML = effect..newparam.(@sid == sourceId)[0];
imageId = source..init_from[0];
}
var image:XML = collada.library_images.image.(@id == imageId)[0];
filename = image.init_from;
if (filename.substr(0, 2) == "./")
{
filename = filename.substr( 2 );
}
}
return filename;
}
/**
* Retrieves the color of a material
*/
private function parseColorMaterial(name:String, materialData:MaterialData):void
{
var material:XML = collada.library_materials.material.(@id == name)[0];
if (material) {
var effectId:String = getId( material.instance_effect.@url );
var effect:XML = collada.library_effects.effect.(@id == effectId)[0];
materialData.ambientColor = getColorValue(effect..ambient[0]);
materialData.diffuseColor = getColorValue(effect..diffuse[0]);
materialData.specularColor = getColorValue(effect..specular[0]);
materialData.shininess = Number(effect..shininess.float[0]);
}
}
private function getColorValue(color:XML):uint
{
if (!color || color.length() == 0)
return 0xFFFFFF;
var colorArray:Array = color.color.split(" ");
var colorString:String = (colorArray[0]*255).toString(16) + (colorArray[1]*255).toString(16) + (colorArray[2]*255).toString(16);
return parseInt(colorString, 16);
}
/**
* Converts a data string to an array of objects. Handles vertex and uv objects
*/
private function deserialize(input:XML, geo:XML, VObject:Class, output:Array):Array
{
var id:String = input.@source.split("#")[1];
// Source?
var acc:XMLList = geo..source.(@id == id).technique_common.accessor;
if (acc != new XMLList())
{
// Build source floats array
var floId:String = acc.@source.split("#")[1];
var floXML:XMLList = collada..float_array.(@id == floId);
var floStr:String = floXML.toString();
var floats:Array = getArray(floStr);
var float:Number;
// Build params array
var params:Array = [];
var param:String;
for each (var par:XML in acc.param)
params.push(par.@name);
// Build output array
var count:int = acc.@count;
var stride:int = acc.@stride;
var len:int = floats.length;
var i:int = 0;
while (i < len)
{
var element:ValueObject = new VObject();
if (element is Vertex) {
var vertex:Vertex = element as Vertex;
for each (param in params) {
float = floats[i];
switch (param) {
case VALUE_X:
if (yUp)
vertex._x = -float*scaling;
else
vertex._x = float*scaling;
break;
case VALUE_Y:
vertex._y = float*scaling;
break;
break;
case VALUE_Z:
vertex._z = float*scaling;
break;
break;
default:
}
i++;
}
} else if (element is UV) {
var uv:UV = element as UV;
for each (param in params) {
float = floats[i];
switch (param) {
case VALUE_U:
uv._u = float;
break;
case VALUE_V:
uv._v = float;
break;
default:
}
i++;
}
}
output.push(element);
}
}
else
{
// Store indexes if no source
var recursive :XMLList = geo..vertices.(@id == id)["input"];
output = deserialize(recursive[0], geo, VObject, output);
}
return output;
}
}
}