/** * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. * You may add additional accurate notices of copyright ownership. * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ package alternativa.engine3d.loaders { import alternativa.engine3d.alternativa3d; import alternativa.engine3d.animation.AnimationClip; import alternativa.engine3d.animation.keys.Track; import alternativa.engine3d.animation.keys.TransformKey; import alternativa.engine3d.animation.keys.TransformTrack; import alternativa.engine3d.core.BoundBox; import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.VertexAttributes; import alternativa.engine3d.core.VertexStream; import alternativa.engine3d.lights.AmbientLight; import alternativa.engine3d.lights.DirectionalLight; import alternativa.engine3d.lights.OmniLight; import alternativa.engine3d.lights.SpotLight; import alternativa.engine3d.materials.A3DUtils; import alternativa.engine3d.objects.Joint; import alternativa.engine3d.objects.LOD; import alternativa.engine3d.objects.Mesh; import alternativa.engine3d.objects.Skin; import alternativa.engine3d.objects.Sprite3D; import alternativa.engine3d.resources.ExternalTextureResource; import alternativa.engine3d.resources.Geometry; import alternativa.types.Long; import commons.A3DMatrix; import commons.Id; import flash.geom.Matrix3D; import flash.geom.Orientation3D; import flash.geom.Vector3D; import flash.utils.ByteArray; import flash.utils.Dictionary; import flash.utils.Endian; import versions.version1.a3d.A3D; import versions.version1.a3d.geometry.A3DGeometry; import versions.version1.a3d.geometry.A3DIndexBuffer; import versions.version1.a3d.geometry.A3DVertexBuffer; import versions.version1.a3d.id.ParentId; import versions.version1.a3d.materials.A3DImage; import versions.version1.a3d.materials.A3DMap; import versions.version1.a3d.materials.A3DMaterial; import versions.version1.a3d.objects.A3DBox; import versions.version1.a3d.objects.A3DObject; import versions.version1.a3d.objects.A3DSurface; import versions.version2.a3d.A3D2; import versions.version2.a3d.A3D2Extra1; import versions.version2.a3d.A3D2Extra2; import versions.version2.a3d.animation.A3D2AnimationClip; import versions.version2.a3d.animation.A3D2Keyframe; import versions.version2.a3d.animation.A3D2Track; import versions.version2.a3d.geometry.A3D2IndexBuffer; import versions.version2.a3d.geometry.A3D2VertexAttributes; import versions.version2.a3d.geometry.A3D2VertexBuffer; import versions.version2.a3d.materials.A3D2CubeMap; import versions.version2.a3d.materials.A3D2Image; import versions.version2.a3d.materials.A3D2Map; import versions.version2.a3d.materials.A3D2Material; import versions.version2.a3d.objects.A3D2AmbientLight; import versions.version2.a3d.objects.A3D2Box; import versions.version2.a3d.objects.A3D2DirectionalLight; import versions.version2.a3d.objects.A3D2Joint; import versions.version2.a3d.objects.A3D2JointBindTransform; import versions.version2.a3d.objects.A3D2LOD; import versions.version2.a3d.objects.A3D2Layer; import versions.version2.a3d.objects.A3D2Mesh; import versions.version2.a3d.objects.A3D2Object; import versions.version2.a3d.objects.A3D2OmniLight; import versions.version2.a3d.objects.A3D2Skin; import versions.version2.a3d.objects.A3D2SpotLight; import versions.version2.a3d.objects.A3D2Sprite; import versions.version2.a3d.objects.A3D2Surface; import versions.version2.a3d.objects.A3D2Transform; use namespace alternativa3d; /** * Base class for classes, that perform parsing of scenes of different formats. */ public class Parser { /** * List of root objects. Root objects are objects, that have no parents. * @see alternativa.engine3d.core.Object3D */ public var hierarchy:Vector.; /** * List of objects, that are got after parsing. * @see alternativa.engine3d.core.Object3D */ public var objects:Vector.; /** * Array of animations. */ public var animations:Vector.; /** * List of all materials assigned to objects, that are got after parsing. * @see alternativa.engine3d.loaders.ParserMaterial */ public var materials:Vector.; private var maps:Dictionary; private var cubemaps:Dictionary; /** * @private */ alternativa3d var layersMap:Dictionary; /** * @private */ alternativa3d var layers:Vector.; /** * @private */ alternativa3d var compressedBuffers:Boolean = false; private var parsedMaterials:Dictionary; private var parsedGeometries:Object; private var unpackedBuffers:Dictionary; /** * Returns object from array objects by name. */ public function getObjectByName(name:String):Object3D { for each (var object:Object3D in objects) { if (object.name == name) return object; } return null; } /** * Returns name of layer for specified object. */ public function getLayerByObject(object:Object3D):String { return layersMap[object]; } /** * Erases all links to external objects. */ public function clean():void { hierarchy = null; objects = null; materials = null; animations = null; layersMap = null; objectsMap = null; a3DBoxes = null; parents = null; layers = null; } /** * @private */ alternativa3d function init():void { hierarchy = new Vector.(); objects = new Vector.(); materials = new Vector.(); animations = new Vector.(); layersMap = new Dictionary(true); layers = new Vector.(); } protected function complete(a3d:Object):void { init(); if (a3d is A3D) { doParse2_0(convert1_2(A3D(a3d))); } else if (a3d is A3D2) { doParse2_0(A3D2(a3d)); } else if (a3d is Vector.) { var vec:Vector. = a3d as Vector.; var len:int = vec.length; for (var i:int = 0; i < len; i++) { doParsePart(vec[i]); } } completeHierarchy(); } private function doParsePart(a3d:Object):void { if (a3d is A3D) { doParse2_0(convert1_2(A3D(a3d))); } else if (a3d is A3D2) { doParse2_0(A3D2(a3d)); } else if (a3d is A3D2Extra1) { doParseExtra1(A3D2Extra1(a3d)); } else if (a3d is A3D2Extra2) { doParseExtra2(A3D2Extra2(a3d)); } } private function doParseExtra1(a3d:A3D2Extra1):void { var layersVec:Vector. = a3d.layers; for each (var layer:A3D2Layer in layersVec) { var layerName:String = (layer.name == null || layer.name.length == 0) ? "default" : layer.name; layers.push(layerName); for each (var id:Long in layer.objects) { if (objectsMap[id] != null) { layersMap[objectsMap[id]] = layerName; } } } } private function doParseExtra2(a3d:A3D2Extra2):void { var lodsVec:Vector. = a3d.lods; for each (var lod:A3D2LOD in lodsVec) { var resObject:LOD = new LOD(); resObject.visible = lod.visible; resObject.name = lod.name; parents[resObject] = lod.parentId; objectsMap[lod.id] = resObject; var length:uint = lod.objects.length; for (var i:int = 0; i < length; i++) { resObject.addLevel(objectsMap[lod.objects[i]], lod.distances[i]); } decomposeTransformation(lod.transform, resObject); } } private var objectsMap:Dictionary; private var parents:Dictionary = new Dictionary(); private var a3DBoxes:Dictionary = new Dictionary(); private function doParse2_0(a3d:A3D2):void { maps = new Dictionary(); cubemaps = new Dictionary(); parsedMaterials = new Dictionary(); parsedGeometries = new Dictionary(); unpackedBuffers = new Dictionary(); objectsMap = new Dictionary(); parents = new Dictionary(); a3DBoxes = new Dictionary(); var parsedTracks:Dictionary = new Dictionary(); var a3DIndexBuffers:Dictionary = new Dictionary(); var a3DVertexBuffers:Dictionary = new Dictionary(); var a3DMaterials:Dictionary = new Dictionary(); var a3DMaps:Dictionary = new Dictionary(); var a3DImages:Dictionary = new Dictionary(); var a3DCubeMaps:Dictionary = new Dictionary(); var a3DObject:A3D2Object; var a3DMesh:A3D2Mesh; var a3DIndexBuffer:A3D2IndexBuffer; var a3DVertexBuffer:A3D2VertexBuffer; var a3DMaterial:A3D2Material; var a3DBox:A3D2Box; var a3DMap:A3D2Map; var a3DImage:A3D2Image; var a3DAmbientLight:A3D2AmbientLight; var a3DOmniLight:A3D2OmniLight; var a3DSpotLight:A3D2SpotLight; var a3DDirLight:A3D2DirectionalLight; var a3DSkin:A3D2Skin; var a3DJoint:A3D2Joint; var a3DSprite:A3D2Sprite; var a3DCubeMap:A3D2CubeMap; for each(a3DIndexBuffer in a3d.indexBuffers) { a3DIndexBuffers[a3DIndexBuffer.id] = a3DIndexBuffer; } for each (var a3DTrack:A3D2Track in a3d.animationTracks) { var resTrack:TransformTrack = new TransformTrack(a3DTrack.objectName); for each (var a3DKeyFrame:A3D2Keyframe in a3DTrack.keyframes) { var tFrame:TransformKey = new TransformKey(); tFrame._time = a3DKeyFrame.time; var components:Vector. = getMatrix3D(a3DKeyFrame.transform).decompose(Orientation3D.QUATERNION); tFrame.x = components[0].x; tFrame.y = components[0].y; tFrame.z = components[0].z; tFrame.rotation = components[1]; tFrame.scaleX = components[2].x; tFrame.scaleY = components[2].y; tFrame.scaleZ = components[2].z; resTrack.addKeyToList(tFrame); } parsedTracks[a3DTrack.id] = resTrack; } var animationClip:AnimationClip; // Animation parsing if (a3d.animationTracks != null && a3d.animationTracks.length > 0) { if (a3d.animationClips == null || a3d.animationClips.length == 0) { animationClip = new AnimationClip(); for each (resTrack in parsedTracks) { animationClip.addTrack(resTrack); } animations.push(animationClip); } else { for each (var a3DAnim:A3D2AnimationClip in a3d.animationClips) { animationClip = new AnimationClip(a3DAnim.name); animationClip.loop = a3DAnim.loop; for each (var trackID:int in a3DAnim.tracks) { var track:Track = parsedTracks[trackID]; if (track != null) { animationClip.addTrack(track); } } animations.push(animationClip); } } } for each (a3DVertexBuffer in a3d.vertexBuffers) { a3DVertexBuffers[a3DVertexBuffer.id] = a3DVertexBuffer; } for each (a3DBox in a3d.boxes) { a3DBoxes[a3DBox.id] = a3DBox; } for each (a3DMaterial in a3d.materials) { a3DMaterials[a3DMaterial.id] = a3DMaterial; } for each (a3DMap in a3d.maps) { a3DMaps[a3DMap.id] = a3DMap; } for each (a3DCubeMap in a3d.cubeMaps) { a3DCubeMaps[a3DCubeMap.id] = a3DCubeMap; } for each (a3DImage in a3d.images) { a3DImages[a3DImage.id] = a3DImage; } var jointsMap:Dictionary = new Dictionary(); for each (a3DJoint in a3d.joints) { var resJoint:Joint = new Joint(); resJoint.visible = a3DJoint.visible; resJoint.name = a3DJoint.name; parents[resJoint] = a3DJoint.parentId; jointsMap[a3DJoint.id] = resJoint; decomposeTransformation(a3DJoint.transform, resJoint); a3DBox = a3DBoxes[a3DJoint.boundBoxId]; if (a3DBox != null) { parseBoundBox(a3DBox.box, resJoint); } } for each (a3DObject in a3d.objects) { var resObject:Object3D = new Object3D(); resObject.visible = a3DObject.visible; resObject.name = a3DObject.name; parents[resObject] = a3DObject.parentId; objectsMap[a3DObject.id] = resObject; jointsMap[a3DObject.id] = resObject; decomposeTransformation(a3DObject.transform, resObject); a3DBox = a3DBoxes[a3DObject.boundBoxId]; if (a3DBox != null) { parseBoundBox(a3DBox.box, resObject); } } for each (a3DSprite in a3d.sprites) { var resSprite:Sprite3D = new Sprite3D(a3DSprite.width, a3DSprite.height); resSprite.material = parseMaterial(a3DMaterials[a3DSprite.materialId], a3DMaps, a3DCubeMaps, a3DImages); resSprite.originX = a3DSprite.originX; resSprite.originY = a3DSprite.originY; resSprite.perspectiveScale = a3DSprite.perspectiveScale; resSprite.alwaysOnTop = a3DSprite.alwaysOnTop; resSprite.rotation = a3DSprite.rotation; objectsMap[a3DSprite.id] = resSprite; decomposeTransformation(a3DSprite.transform, resSprite); } for each (a3DSkin in a3d.skins) { var resSkin:Mesh = parseSkin(a3DSkin, jointsMap, parents, a3DIndexBuffers, a3DVertexBuffers, a3DMaterials, a3DMaps, a3DCubeMaps, a3DImages); resSkin.visible = a3DSkin.visible; resSkin.name = a3DSkin.name; objectsMap[a3DSkin.id] = resSkin; //The transformation should not affect skin (Due collada comatibility) //decomposeTransformation(a3DSkin.transform, resSkin); a3DBox = a3DBoxes[a3DSkin.boundBoxId]; if (a3DBox != null) { parseBoundBox(a3DBox.box, resSkin); } } for each (a3DAmbientLight in a3d.ambientLights) { var resAmbientLight:AmbientLight = new AmbientLight(a3DAmbientLight.color); resAmbientLight.intensity = a3DAmbientLight.intensity; resAmbientLight.visible = a3DAmbientLight.visible; resAmbientLight.name = a3DAmbientLight.name; parents[resAmbientLight] = a3DAmbientLight.parentId; objectsMap[a3DAmbientLight.id] = resAmbientLight; decomposeTransformation(a3DAmbientLight.transform, resAmbientLight); a3DBox = a3DBoxes[a3DAmbientLight.boundBoxId]; if (a3DBox != null) { parseBoundBox(a3DBox.box, resAmbientLight); } } for each (a3DOmniLight in a3d.omniLights) { var resOmniLight:OmniLight = new OmniLight(a3DOmniLight.color, a3DOmniLight.attenuationBegin, a3DOmniLight.attenuationEnd); resOmniLight.intensity = a3DOmniLight.intensity; resOmniLight.visible = a3DOmniLight.visible; resOmniLight.name = a3DOmniLight.name; parents[resOmniLight] = a3DOmniLight.parentId; objectsMap[a3DOmniLight.id] = resOmniLight; decomposeTransformation(a3DOmniLight.transform, resOmniLight); } for each (a3DSpotLight in a3d.spotLights) { var resSpotLight:SpotLight = new SpotLight(a3DSpotLight.color, a3DSpotLight.attenuationBegin, a3DSpotLight.attenuationEnd, a3DSpotLight.hotspot, a3DSpotLight.falloff); resSpotLight.intensity = a3DSpotLight.intensity; resSpotLight.visible = a3DSpotLight.visible; resSpotLight.name = a3DSpotLight.name; parents[resSpotLight] = a3DSpotLight.parentId; objectsMap[a3DSpotLight.id] = resSpotLight; decomposeTransformation(a3DSpotLight.transform, resSpotLight); } for each(a3DDirLight in a3d.directionalLights) { var resDirLight:DirectionalLight = new DirectionalLight(a3DDirLight.color); resDirLight.visible = a3DDirLight.visible; resDirLight.name = a3DDirLight.name; parents[resDirLight] = a3DDirLight.parentId; objectsMap[a3DDirLight.id] = resDirLight; decomposeTransformation(a3DDirLight.transform, resDirLight); } for each (a3DMesh in a3d.meshes) { var resMesh:Mesh = parseMesh(a3DMesh, a3DIndexBuffers, a3DVertexBuffers, a3DMaterials, a3DMaps, a3DCubeMaps, a3DImages); resMesh.visible = a3DMesh.visible; resMesh.name = a3DMesh.name; parents[resMesh] = a3DMesh.parentId; objectsMap[a3DMesh.id] = resMesh; decomposeTransformation(a3DMesh.transform, resMesh); a3DBox = a3DBoxes[a3DMesh.boundBoxId]; if (a3DBox != null) { parseBoundBox(a3DBox.box, resMesh); } } maps = null; parsedMaterials = null; parsedGeometries = null; } private function completeHierarchy():void { var parent:Long; var p:Object3D; var object:Object3D; for each (object in objectsMap) { objects.push(object); if (object.parent != null) continue; parent = parents[object]; if (parent != null) { p = objectsMap[parent]; if (p != null) { p.addChild(object); } else { hierarchy.push(object); } } else { hierarchy.push(object); } } } private function parseBoundBox(box:Vector., destination:Object3D):void { destination.boundBox = new BoundBox(); destination.boundBox.minX = box[0]; destination.boundBox.minY = box[1]; destination.boundBox.minZ = box[2]; destination.boundBox.maxX = box[3]; destination.boundBox.maxY = box[4]; destination.boundBox.maxZ = box[5]; } private final function unpackVertexBuffer(buffer:ByteArray):void { var tempBuffer:ByteArray = new ByteArray(); tempBuffer.endian = Endian.LITTLE_ENDIAN; buffer.position = 0; while (buffer.bytesAvailable > 0) { var data:uint = buffer.readUnsignedShort(); var vi:uint = data; vi &= 0x7FFF; vi ^= (vi + 0x1c000) ^ vi; vi = vi << 13; tempBuffer.writeUnsignedInt(data > 0x8000 ? vi | 0x80000000 : vi); } buffer.position = 0; buffer.writeBytes(tempBuffer); } private function getMatrix3D(transform:A3D2Transform):Matrix3D { if (transform == null) return null; var matrix:A3DMatrix = transform.matrix; return new Matrix3D(Vector.( [matrix.a, matrix.e, matrix.i, 0, matrix.b, matrix.f, matrix.j, 0, matrix.c, matrix.g, matrix.k, 0, matrix.d, matrix.h, matrix.l, 1 ])); } private function decomposeTransformation(transform:A3D2Transform, obj:Object3D):void { if (transform == null) return; var mat:Matrix3D = getMatrix3D(transform); var vecs:Vector. = mat.decompose(); obj.x = vecs[0].x; obj.y = vecs[0].y; obj.z = vecs[0].z; obj.rotationX = vecs[1].x; obj.rotationY = vecs[1].y; obj.rotationZ = vecs[1].z; obj.scaleX = vecs[2].x; obj.scaleY = vecs[2].y; obj.scaleZ = vecs[2].z; } private function decomposeBindTransformation(transform:A3D2Transform, obj:Joint):void { if (transform == null) return; var matrix:A3DMatrix = transform.matrix; var mat:Vector. = Vector.([ matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f, matrix.g, matrix.h, matrix.i, matrix.j, matrix.k, matrix.l] ); obj.setBindPoseMatrix(mat); } private function parseMesh(a3DMesh:A3D2Mesh, indexBuffers:Dictionary, vertexBuffers:Dictionary, materials:Dictionary, a3DMaps:Dictionary, a3DCubeMaps:Dictionary, images:Dictionary):Mesh { var res:Mesh = new Mesh(); res.geometry = parseGeometry(a3DMesh.indexBufferId, a3DMesh.vertexBuffers, indexBuffers, vertexBuffers); var surfaces:Vector. = a3DMesh.surfaces; for (var i:int = 0; i < surfaces.length; i++) { var s:A3D2Surface = surfaces[i]; var m:ParserMaterial = parseMaterial(materials[s.materialId], a3DMaps, a3DCubeMaps, images); res.addSurface(m, s.indexBegin, s.numTriangles); } return res; } private function parseSkin(a3DSkin:A3D2Skin, jointsMap:Dictionary, parents:Dictionary, indexBuffers:Dictionary, vertexBuffers:Dictionary, materials:Dictionary, a3DMaps:Dictionary, a3DCubeMaps:Dictionary, images:Dictionary):Skin { var geometry:Geometry = parseGeometry(a3DSkin.indexBufferId, a3DSkin.vertexBuffers, indexBuffers, vertexBuffers); var res:Skin = new Skin(getNumInfluences(geometry)); res.geometry = geometry; var surfaces:Vector. = a3DSkin.surfaces; for (var i:int = 0; i < surfaces.length; i++) { var s:A3D2Surface = surfaces[i]; var m:ParserMaterial = parseMaterial(materials[s.materialId], a3DMaps, a3DCubeMaps, images); res.addSurface(m, s.indexBegin, s.numTriangles); } copyBones(res, a3DSkin, jointsMap, parents); return res; } private function copyBones(skin:Skin, a3DSkin:A3D2Skin, jointsMap:Dictionary, parents:Dictionary):void { var rootBones:Vector. = new Vector.(); var s2dMap:Dictionary = new Dictionary(); var sourceJoints:Dictionary = new Dictionary(); var jointIDs:Dictionary = new Dictionary(); var joint:Joint; var object:Object3D; var indexOffset:uint = 0; var dJoint:Joint; for each (var numJoints:uint in a3DSkin.numJoints) { for (var i:int = 0; i < numJoints; i++) { var key:Long = a3DSkin.joints[int(indexOffset + i)]; object = jointsMap[key]; sourceJoints[key] = object; jointIDs[object] = key; } indexOffset += numJoints; } for (var idk:* in sourceJoints) { object = sourceJoints[idk]; if (object == null) { throw new Error("Joint for skin " + a3DSkin.name + " not found"); } delete objectsMap[idk]; s2dMap[object] = cloneJoint(object); } var count:int; indexOffset = 0; for (i = 0, count = a3DSkin.numJoints.length; i < count; i++) { numJoints = a3DSkin.numJoints[i]; skin.surfaceJoints[i] = new Vector.(); for (var j:int = 0; j < numJoints; j++) { skin.surfaceJoints[i].push(s2dMap[sourceJoints[a3DSkin.joints[int(indexOffset + j)]]]); } indexOffset += numJoints; } skin.calculateSurfacesProcedures(); for (i = 0; i < a3DSkin.jointBindTransforms.length; i++) { var bindPose:A3D2JointBindTransform = a3DSkin.jointBindTransforms[i]; //Joint is not affect to vertices, but affect on transformation of other joints (due to hierarchy). if (sourceJoints[bindPose.id] == null) { object = jointsMap[bindPose.id]; sourceJoints[bindPose.id] = object; s2dMap[object] = cloneJoint(object); } decomposeBindTransformation(bindPose.bindPoseTransform, Joint(s2dMap[sourceJoints[bindPose.id]])); } var skinParent:Long = null; for each(object in sourceJoints) { dJoint = s2dMap[object]; var parent:Long = parents[object]; if (isRootJointNode(object, parents, sourceJoints, jointsMap)) { skinParent = parent; rootBones.push(dJoint); } else { var pJointSource:Object3D = jointsMap[parent]; var pJoint:Joint = s2dMap[pJointSource]; if (pJoint == null) { attachJoint(dJoint, object, parents, jointsMap, s2dMap); } else { pJoint.addChild(dJoint); } } } if (skinParent != null) { parents[skin] = skinParent; } skin._renderedJoints = new Vector.(); for (i = 0; i < numJoints; i++) { skin._renderedJoints.push(s2dMap[sourceJoints[a3DSkin.joints[i]]]); } for each(joint in rootBones) { skin.addChild(joint); } } private function attachJoint(joint:Joint, source:Object3D, parents:Dictionary, sourceJoints:Dictionary, s2dMap:Dictionary):void { var parentID:Long = parents[source]; var parentSource:Object3D = sourceJoints[parentID]; var parentDestination:Joint = s2dMap[parentSource]; if (parentDestination == null) { s2dMap[parentSource] = parentDestination = cloneJoint(parentSource); delete objectsMap[parentID]; attachJoint(parentDestination, parentSource, parents, sourceJoints, s2dMap); } parentDestination.addChild(joint); } private function isRootJointNode(joint:Object3D, parents:Dictionary, joints:Dictionary, jointsMap:Dictionary):Boolean { var parent:Long = parents[joint]; while (parent != null) { var current:Object3D = jointsMap[parent]; if (joints[parent] != null) { return false; } parent = parents[current]; } return true; } private function cloneJoint(source:Object3D):Joint { var result:Joint = new Joint(); result.name = source.name; result.visible = source.visible; result.boundBox = source.boundBox ? source.boundBox.clone() : null; result._x = source._x; result._y = source._y; result._z = source._z; result._rotationX = source._rotationX; result._rotationY = source._rotationY; result._rotationZ = source._rotationZ; result._scaleX = source._scaleX; result._scaleY = source._scaleY; result._scaleZ = source._scaleZ; result.composeTransforms(); return result; } private function getNumInfluences(geometry:Geometry):uint { var result:uint = 0; for (var i:int = 0, count:int = VertexAttributes.JOINTS.length; i < count; i++) { if (geometry.hasAttribute(VertexAttributes.JOINTS[i])) { result += 2; } } return result; } private function parseGeometry(indexBufferID:int, vertexBuffersIDs:Vector., indexBuffers:Dictionary, vertexBuffers:Dictionary):Geometry { var key:String = "i" + indexBufferID.toString(); for each(var id:int in vertexBuffersIDs) { key += "v" + id.toString(); } var geometry:Geometry = parsedGeometries[key]; if (geometry != null) return geometry; geometry = new Geometry(); var a3dIB:A3D2IndexBuffer = indexBuffers[indexBufferID]; var indices:Vector. = A3DUtils.byteArrayToVectorUint(a3dIB.byteBuffer); var uvoffset:int = 0; geometry._indices = indices; var buffers:Vector. = vertexBuffersIDs; var vertexCount:uint; for (var j:int = 0; j < buffers.length; j++) { var buffer:A3D2VertexBuffer = vertexBuffers[buffers[j]]; if (compressedBuffers) { if (unpackedBuffers[buffer] == null) { unpackVertexBuffer(buffer.byteBuffer); unpackedBuffers[buffer] = true; } } vertexCount = buffer.vertexCount; var byteArray:ByteArray = buffer.byteBuffer; byteArray.endian = Endian.LITTLE_ENDIAN; var offset:int = 0; var attributes:Array = []; var jointsOffset:int = 0; for (var k:int = 0; k < buffer.attributes.length; k++) { var attr:int; switch (buffer.attributes[k]) { case A3D2VertexAttributes.POSITION: attr = VertexAttributes.POSITION; break; case A3D2VertexAttributes.NORMAL: attr = VertexAttributes.NORMAL; break; case A3D2VertexAttributes.TANGENT4: attr = VertexAttributes.TANGENT4; break; case A3D2VertexAttributes.TEXCOORD: attr = VertexAttributes.TEXCOORDS[uvoffset]; uvoffset++; break; case A3D2VertexAttributes.JOINT: attr = VertexAttributes.JOINTS[jointsOffset]; jointsOffset++; break; } var numFloats:int = VertexAttributes.getAttributeStride(attr); numFloats = (numFloats < 1) ? 1 : numFloats; for (var t:int = 0; t < numFloats; t++) { attributes[offset] = attr; offset++; } } geometry.addVertexStream(attributes); geometry._vertexStreams[0].data = byteArray; } geometry._numVertices = (buffers.length > 0) ? vertexCount : 0; parsedGeometries[key] = geometry; return geometry; } private function parseMap(source:A3D2Map, images:Dictionary):ExternalTextureResource { if (source == null) return null; var res:ExternalTextureResource = maps[source.imageId]; if (res != null) return res; res = maps[source.imageId] = new ExternalTextureResource(images[source.imageId].url); return res; } private function parseCubeMap(source:A3D2CubeMap, images:Dictionary):ExternalTextureResource { return null; } private function parseMaterial(source:A3D2Material, a3DMaps:Dictionary, a3DCubeMaps:Dictionary, images:Dictionary):ParserMaterial { if (source == null) return null; var res:ParserMaterial = parsedMaterials[source.id]; if (res != null) return res; res = parsedMaterials[source.id] = new ParserMaterial(); res.textures["diffuse"] = parseMap(a3DMaps[source.diffuseMapId], images); res.textures["emission"] = parseMap(a3DMaps[source.lightMapId], images); res.textures["bump"] = parseMap(a3DMaps[source.normalMapId], images); res.textures["specular"] = parseMap(a3DMaps[source.specularMapId], images); res.textures["glossiness"] = parseMap(a3DMaps[source.glossinessMapId], images); res.textures["transparent"] = parseMap(a3DMaps[source.opacityMapId], images); res.textures["reflection"] = parseCubeMap(a3DCubeMaps[source.reflectionCubeMapId], images); materials.push(res); return res; } private static function convert1_2(source:A3D):A3D2 { // source.boxes var sourceBoxes:Vector. = source.boxes; var destBoxes:Vector. = null; if (sourceBoxes != null) { destBoxes = new Vector.(); for (var i:int = 0, count:int = sourceBoxes.length; i < count; i++) { var sourceBox:A3DBox = sourceBoxes[i]; var destBox:A3D2Box = new A3D2Box(sourceBox.box, sourceBox.id.id); destBoxes[i] = destBox; } } // source.geometries var sourceGeometries:Dictionary = new Dictionary(); if (source.geometries != null) { for each(var sourceGeometry:A3DGeometry in source.geometries) { sourceGeometries[sourceGeometry.id.id] = sourceGeometry; } } // source.images var sourceImages:Vector. = source.images; var destImages:Vector. = null; if (sourceImages != null) { destImages = new Vector.(); for (i = 0, count = sourceImages.length; i < count; i++) { var sourceImage:A3DImage = sourceImages[i]; var destImage:A3D2Image = new A3D2Image(sourceImage.id.id, sourceImage.url); destImages[i] = destImage; } } // source.maps var sourceMaps:Vector. = source.maps; var destMaps:Vector. = null; if (sourceMaps != null) { destMaps = new Vector.(); for (i = 0, count = sourceMaps.length; i < count; i++) { var sourceMap:A3DMap = sourceMaps[i]; var destMap:A3D2Map = new A3D2Map(sourceMap.channel, sourceMap.id.id, sourceMap.imageId.id); destMaps[i] = destMap; } } // source.materials var sourceMaterials:Vector. = source.materials; var destMaterials:Vector. = null; if (sourceMaterials != null) { destMaterials = new Vector.(); for (i = 0, count = sourceMaterials.length; i < count; i++) { var sourceMaterial:A3DMaterial = sourceMaterials[i]; var destMaterial:A3D2Material = new A3D2Material( idToInt(sourceMaterial.diffuseMapId), idToInt(sourceMaterial.glossinessMapId), idToInt(sourceMaterial.id), idToInt(sourceMaterial.lightMapId), idToInt(sourceMaterial.normalMapId), idToInt(sourceMaterial.opacityMapId), -1, idToInt(sourceMaterial.specularMapId) ); destMaterials[i] = destMaterial; } } // source.objects var sourceObjects:Vector. = source.objects; var destObjects:Vector. = null; var destMeshes:Vector. = null; var destVertexBuffers:Vector. = null; var destIndexBuffers:Vector. = null; var lastIndexBufferIndex:uint = 0; var lastVertexBufferIndex:uint = 0; var objectsMap:Dictionary = new Dictionary(); if (sourceObjects != null) { destMeshes = new Vector.(); destObjects = new Vector.(); destVertexBuffers = new Vector.(); destIndexBuffers = new Vector.(); for (i = 0, count = sourceObjects.length; i < count; i++) { var sourceObject:A3DObject = sourceObjects[i]; if (sourceObject.surfaces != null && sourceObject.surfaces.length > 0) { var destMesh:A3D2Mesh = null; sourceGeometry = sourceGeometries[sourceObject.geometryId.id]; var destIndexBufferId:int = -1; var destVertexBuffersIds:Vector. = new Vector.(); if (sourceGeometry != null) { var sourceIndexBuffer:A3DIndexBuffer = sourceGeometry.indexBuffer; var sourceVertexBuffers:Vector. = sourceGeometry.vertexBuffers; var destIndexBuffer:A3D2IndexBuffer = new A3D2IndexBuffer(sourceIndexBuffer.byteBuffer, lastIndexBufferIndex++, sourceIndexBuffer.indexCount); destIndexBufferId = destIndexBuffer.id; destIndexBuffers.push(destIndexBuffer); for (var j:int = 0, inCount:int = sourceVertexBuffers.length; j < inCount; j++) { var sourceVertexBuffer:A3DVertexBuffer = sourceVertexBuffers[j]; var sourceAttributes:Vector. = sourceVertexBuffer.attributes; var destAttributes:Vector. = new Vector.(); for (var k:int = 0, kCount:int = sourceAttributes.length; k < kCount; k++) { var attr:int = sourceAttributes[k]; switch (attr) { case 0: destAttributes[k] = A3D2VertexAttributes.POSITION; break; case 1: destAttributes[k] = A3D2VertexAttributes.NORMAL; break; case 2: destAttributes[k] = A3D2VertexAttributes.TANGENT4; break; case 3: break; case 4: break; case 5: destAttributes[k] = A3D2VertexAttributes.TEXCOORD; break; } } var destVertexBuffer:A3D2VertexBuffer = new A3D2VertexBuffer( destAttributes, sourceVertexBuffer.byteBuffer, lastVertexBufferIndex++, sourceVertexBuffer.vertexCount ); destVertexBuffers.push(destVertexBuffer); destVertexBuffersIds.push(destVertexBuffer.id); } } destMesh = new A3D2Mesh( idToInt(sourceObject.boundBoxId), idToLong(sourceObject.id), destIndexBufferId, sourceObject.name, convertParent1_2(sourceObject.parentId), convertSurfaces1_2(sourceObject.surfaces), new A3D2Transform(sourceObject.transformation.matrix), destVertexBuffersIds, sourceObject.visible ); destMeshes.push(destMesh); objectsMap[sourceObject.id.id] = destMesh; } else { var destObject:A3D2Object = new A3D2Object( idToInt(sourceObject.boundBoxId), idToLong(sourceObject.id), sourceObject.name, convertParent1_2(sourceObject.parentId), new A3D2Transform(sourceObject.transformation.matrix), sourceObject.visible ); destObjects.push(destObject); objectsMap[sourceObject.id.id] = destObject; } } } var result:A3D2 = new A3D2( null, null, null, destBoxes, null, null, null, destImages, destIndexBuffers, null, destMaps, destMaterials, destMeshes != null && destMeshes.length > 0 ? destMeshes : null, destObjects != null && destObjects.length > 0 ? destObjects : null, null, null, null, null, destVertexBuffers ); return result; } private static function idToInt(id:Id):int { return id != null ? id.id : -1; } private static function idToLong(id:Id):Long { return id != null ? Long.fromInt(id.id) : Long.fromInt(-1); } private static function convertParent1_2(parentId:ParentId):Long { if (parentId == null) return null; return parentId != null ? Long.fromInt(parentId.id) : null; } private static function convertSurfaces1_2(source:Vector.):Vector. { var dest:Vector. = new Vector.(); for (var i:int = 0, count:int = source.length; i < count; i++) { var sourceSurface:A3DSurface = source[i]; var destSurface:A3D2Surface = new A3D2Surface( sourceSurface.indexBegin, idToInt(sourceSurface.materialId), sourceSurface.numTriangles); dest[i] = destSurface; } return dest; } /** * @private */ alternativa3d static function traceGeometry(geometry:Geometry):void { var vertexStream:VertexStream = geometry._vertexStreams[0]; var prev:int = -1; var attribtuesLength:int = vertexStream.attributes.length; var stride:int = attribtuesLength*4; var length:int = vertexStream.data.length/stride; var data:ByteArray = vertexStream.data; for (var j:int = 0; j < length; j++) { var traceString:String = "V" + j + " "; var offset:int = -4; for (var i:int = 0; i < attribtuesLength; i++) { var attr:int = vertexStream.attributes[i]; var x:Number, y:Number, z:Number; if (attr == prev) continue; offset = geometry.getAttributeOffset(attr)*4; switch (attr) { case VertexAttributes.POSITION: data.position = j*stride + offset; traceString += "P[" + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(2) + "] "; break; case 20: data.position = j*stride + offset; traceString += "A[" + data.readFloat().toString(2) + "]"; break; case VertexAttributes.NORMAL: data.position = j*stride + offset; x = data.readFloat(); y = data.readFloat(); z = data.readFloat(); break; case VertexAttributes.TANGENT4: data.position = j*stride + offset; x = data.readFloat(); y = data.readFloat(); z = data.readFloat(); break; case VertexAttributes.JOINTS[0]: data.position = j*stride + offset; traceString += "J0[" + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + "] "; break; case VertexAttributes.JOINTS[1]: data.position = j*stride + offset; traceString += "J1[" + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + "] "; break; case VertexAttributes.JOINTS[2]: data.position = j*stride + offset; traceString += "J1[" + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + "] "; break; case VertexAttributes.JOINTS[3]: data.position = j*stride + offset; traceString += "J1[" + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + ", " + data.readFloat().toFixed(0) + " = " + data.readFloat().toFixed(2) + "] "; break; } prev = attr; } trace(traceString); } } } }