From be72116dc8d6bf2e062b732b3f6510af7cc21502 Mon Sep 17 00:00:00 2001 From: makc Date: Thu, 29 Nov 2012 09:08:20 +0100 Subject: [PATCH] async dae parsing --- .../engine3d/loaders/ParserCollada.as | 128 ++++++++++++++++++ .../engine3d/loaders/collada/DaeDocument.as | 10 +- .../engine3d/loaders/collada/DaeGeometry.as | 5 +- .../engine3d/loaders/collada/DaeInput.as | 9 +- 4 files changed, 146 insertions(+), 6 deletions(-) diff --git a/src/alternativa/engine3d/loaders/ParserCollada.as b/src/alternativa/engine3d/loaders/ParserCollada.as index e0cacb7..ab9eb0b 100644 --- a/src/alternativa/engine3d/loaders/ParserCollada.as +++ b/src/alternativa/engine3d/loaders/ParserCollada.as @@ -14,10 +14,14 @@ package alternativa.engine3d.loaders { import alternativa.engine3d.core.Light3D; import alternativa.engine3d.core.Object3D; import alternativa.engine3d.loaders.collada.DaeDocument; + import alternativa.engine3d.loaders.collada.DaeElement; + import alternativa.engine3d.loaders.collada.DaeGeometry; import alternativa.engine3d.loaders.collada.DaeMaterial; import alternativa.engine3d.loaders.collada.DaeNode; import alternativa.engine3d.loaders.collada.DaeObject; import alternativa.engine3d.resources.ExternalTextureResource; + import flash.utils.getTimer; + import flash.utils.setTimeout; use namespace alternativa3d; @@ -79,6 +83,43 @@ package alternativa.engine3d.loaders { } } + /** + * Method parses xml of collada asynchronously and fills arrays objects, hierarchy, materials, animations + * It is slower than parse but does not lock the thread. + * If you need to download textures, use class TexturesLoader. + *

Path to collada file should match with URI specification. E.g., file:///C:/test.dae or /C:/test.dae for the full paths and test.dae, ./test.dae in case of relative.

+ * + * @param data XML data type of collada. + * @param onComplete Callback function accepting ParserCollada object as its argument. + * @param baseURL Path to textures relative to swf-file (Or file name only in case of trimPaths=true). + * @param trimPaths Use file names only, without paths. + * + * @see alternativa.engine3d.loaders.TexturesLoader + * @see #objects + * @see #hierarchy + * @see #materials + */ + public function parseAsync(data:XML, onComplete:Function, baseURL:String = null, trimPaths:Boolean = false):void { + init(); + + var document:DaeDocument = new DaeDocument(data, 0); + if (document.scene != null) { + parseMaterials(document.materials, baseURL, trimPaths); + addNodesToQueue(document.scene.nodes, null, false); + + // parse nodes internal sttructures ahead of time to avoid congestion + addElementsToQueue(document.controllers); + addElementsToQueue(document.channels); + addElementsToQueue(document.geometries); + for each (var geom:DaeGeometry in document.geometries) { + addElementsToQueue (geom.primitives); + } + addElementsToQueue(document.sources); + + parseQueuedElements(onComplete); + } + } + /** * Adds components of animated object to lists objects, parents, hierarchy, cameras, animations and to parent container. */ @@ -176,6 +217,85 @@ package alternativa.engine3d.loaders { } } + private var queue:Vector. = new Vector. (); + + private function addNodesToQueue(nodes:Vector., parent:Object3D, skinsOnly:Boolean):void { + for (var i:int = nodes.length; i > 0; i--) { + var args:QueueElement = new QueueElement; + args.element = nodes[i - 1]; + args.parent = parent; + args.skinsOnly = skinsOnly; + queue.unshift(args); + } + } + + private function addElementsToQueue(elements:Object):void { + for each (var element:DaeElement in elements) { + var args:QueueElement = new QueueElement; + args.element = element; + queue.unshift(args); + } + } + + private const ASYNC_LIMIT:int = 50; + private const ASYNC_TIMEOUT:int = 1; + + private function parseQueuedElements(onComplete:Function):void { + var t:int = getTimer (); + do { + if (queue.length == 0) { + // make sure onComplete is called after parseAsync exits + setTimeout (onComplete, ASYNC_TIMEOUT, this); + return; + } + + var args:QueueElement = queue.shift(); + args.element.parse(); + + if (args.element is DaeNode) { + var node:DaeNode = args.element as DaeNode; + var parent:Object3D = args.parent; + var skinsOnly:Boolean = args.skinsOnly; + + // Object to which child objects will be added. + var container:Object3D = null; + if (node.skins != null) { + // Main joint of skin + container = addObjects(node.skins, parent, node.layer); + } else { + if (!skinsOnly && !node.skinOrTopmostJoint) { + if (node.objects != null) { + container = addObjects(node.objects, parent, node.layer); + } else { + // Empty Object3D + container = new Object3D(); + container.name = node.cloneString(node.name); + addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer); + container.calculateBoundBox(); + } + } else { + // Object or its parent is a skin or joint + // Create an object only if there are a child skins + if (hasSkinsInChildren(node)) { + container = new Object3D(); + container.name = node.cloneString(node.name); + addObject(node.applyAnimation(node.applyTransformations(container)), parent, node.layer); + addNodesToQueue(node.nodes, container, skinsOnly || node.skinOrTopmostJoint); + container.calculateBoundBox(); + } + } + } + // Parse children + if (container != null) { + addNodesToQueue(node.nodes, container, skinsOnly || node.skinOrTopmostJoint); + } + } + + } while (getTimer () - t < ASYNC_LIMIT); + + setTimeout (parseQueuedElements, ASYNC_TIMEOUT, onComplete); + } + private function trimPath(path:String):String { var index:int = path.lastIndexOf("/"); return (index < 0) ? path : path.substr(index + 1); @@ -387,3 +507,11 @@ package alternativa.engine3d.loaders { } } } + +import alternativa.engine3d.core.Object3D; +import alternativa.engine3d.loaders.collada.DaeElement; +class QueueElement { + public var element:DaeElement; + public var parent:Object3D; + public var skinsOnly:Boolean; +} \ No newline at end of file diff --git a/src/alternativa/engine3d/loaders/collada/DaeDocument.as b/src/alternativa/engine3d/loaders/collada/DaeDocument.as index 6924f04..165c596 100644 --- a/src/alternativa/engine3d/loaders/collada/DaeDocument.as +++ b/src/alternativa/engine3d/loaders/collada/DaeDocument.as @@ -22,7 +22,7 @@ package alternativa.engine3d.loaders.collada { private var data:XML; // Dictionaries to store matchings id-> DaeElement - internal var sources:Object; + public var sources:Object; internal var arrays:Object; internal var vertices:Object; public var geometries:Object; @@ -30,9 +30,11 @@ package alternativa.engine3d.loaders.collada { internal var lights:Object; internal var images:Object; internal var effects:Object; - internal var controllers:Object; + public var controllers:Object; internal var samplers:Object; + public var channels:Vector.; + public var materials:Object; internal var logger:DaeLogger; @@ -186,6 +188,10 @@ package alternativa.engine3d.loaders.collada { var node:DaeNode = channel.node; if (node != null) { node.addChannel(channel); + if (channels == null) { + channels = new Vector.; + } + channels.push (channel); } } } diff --git a/src/alternativa/engine3d/loaders/collada/DaeGeometry.as b/src/alternativa/engine3d/loaders/collada/DaeGeometry.as index d3d7b62..253c027 100644 --- a/src/alternativa/engine3d/loaders/collada/DaeGeometry.as +++ b/src/alternativa/engine3d/loaders/collada/DaeGeometry.as @@ -26,7 +26,7 @@ package alternativa.engine3d.loaders.collada { use namespace alternativa3d; internal var geometryVertices:Vector.; - internal var primitives:Vector.; + public var primitives:Vector.; internal var geometry:Geometry; private var vertices:DaeVertices; @@ -46,12 +46,13 @@ package alternativa.engine3d.loaders.collada { if (verticesXML != null) { vertices = new DaeVertices(verticesXML, document); document.vertices[vertices.id] = vertices; + // set primitives early for ParserCollada's parseAsync + parsePrimitives(); } } override protected function parseImplementation():Boolean { if (vertices != null) { - parsePrimitives(); vertices.parse(); var numVertices:int = vertices.positions.numbers.length/vertices.positions.stride; diff --git a/src/alternativa/engine3d/loaders/collada/DaeInput.as b/src/alternativa/engine3d/loaders/collada/DaeInput.as index a631114..43aeee9 100644 --- a/src/alternativa/engine3d/loaders/collada/DaeInput.as +++ b/src/alternativa/engine3d/loaders/collada/DaeInput.as @@ -28,9 +28,14 @@ package alternativa.engine3d.loaders.collada { return data.@source[0]; } + // todo: profiler shows that offset getter is seriously abused in DaePrimitive's fillGeometry + private var _offset:int = -1; public function get offset():int { - var attr:XML = data.@offset[0]; - return (attr == null) ? 0 : parseInt(attr.toString(), 10); + if (_offset < 0) { + var attr:XML = data.@offset[0]; + _offset = (attr == null) ? 0 : parseInt(attr.toString(), 10); + } + return _offset; } public function get setNum():int {