diff --git a/src/alternativa/engine3d/loaders/ParserCollada.as b/src/alternativa/engine3d/loaders/ParserCollada.as
index e0cacb7..e112b78 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 onComplete Callback function accepting ParserCollada object as its argument.
+ * @param data XML data type of collada.
+ * @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(onComplete:Function, data:XML, 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,90 @@ package alternativa.engine3d.loaders {
}
}
+ private var queue:Vector. = new Vector. ();
+
+ private function addNodesToQueue(nodes:Vector., parent:Object3D, skinsOnly:Boolean):void {
+ for (var j:int = 0; j < queue.length; j++) {
+ if (queue[j].element is DaeNode) {
+ break;
+ }
+ }
+ 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.splice(j, 0, 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 +512,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 {