/src/away3d/loaders/misc/SingleFileLoader.as

http://github.com/away3d/away3d-core-fp11 · ActionScript · 522 lines · 247 code · 85 blank · 190 comment · 24 complexity · bb830a14ccc0e5da6279219002d506a5 MD5 · raw file

  1. package away3d.loaders.misc
  2. {
  3. import away3d.arcane;
  4. import away3d.events.AssetEvent;
  5. import away3d.events.LoaderEvent;
  6. import away3d.events.ParserEvent;
  7. import away3d.loaders.parsers.ImageParser;
  8. import away3d.loaders.parsers.ParserBase;
  9. import away3d.loaders.parsers.ParserDataFormat;
  10. import flash.events.Event;
  11. import flash.events.EventDispatcher;
  12. import flash.events.IOErrorEvent;
  13. import flash.net.URLLoader;
  14. import flash.net.URLLoaderDataFormat;
  15. import flash.net.URLRequest;
  16. use namespace arcane;
  17. /**
  18. * Dispatched when any asset finishes parsing. Also see specific events for each
  19. * individual asset type (meshes, materials et c.)
  20. *
  21. * @eventType away3d.events.AssetEvent
  22. */
  23. [Event(name="assetComplete", type="away3d.events.AssetEvent")]
  24. /**
  25. * Dispatched when a single dependency (which may be the main file of a resource)
  26. * finishes loading.
  27. *
  28. * @eventType away3d.events.LoaderEvent
  29. */
  30. [Event(name="dependencyComplete", type="away3d.events.LoaderEvent")]
  31. /**
  32. * Dispatched when an error occurs during loading. I
  33. *
  34. * @eventType away3d.events.LoaderEvent
  35. */
  36. [Event(name="loadError", type="away3d.events.LoaderEvent")]
  37. /**
  38. * Dispatched when an error occurs during parsing.
  39. *
  40. * @eventType away3d.events.ParserEvent
  41. */
  42. [Event(name="parseError", type="away3d.events.ParserEvent")]
  43. /**
  44. * Dispatched when a skybox asset has been costructed from a ressource.
  45. *
  46. * @eventType away3d.events.AssetEvent
  47. */
  48. [Event(name="skyboxComplete", type="away3d.events.AssetEvent")]
  49. /**
  50. * Dispatched when a camera3d asset has been costructed from a ressource.
  51. *
  52. * @eventType away3d.events.AssetEvent
  53. */
  54. [Event(name="cameraComplete", type="away3d.events.AssetEvent")]
  55. /**
  56. * Dispatched when a mesh asset has been costructed from a ressource.
  57. *
  58. * @eventType away3d.events.AssetEvent
  59. */
  60. [Event(name="meshComplete", type="away3d.events.AssetEvent")]
  61. /**
  62. * Dispatched when a geometry asset has been constructed from a resource.
  63. *
  64. * @eventType away3d.events.AssetEvent
  65. */
  66. [Event(name="geometryComplete", type="away3d.events.AssetEvent")]
  67. /**
  68. * Dispatched when a skeleton asset has been constructed from a resource.
  69. *
  70. * @eventType away3d.events.AssetEvent
  71. */
  72. [Event(name="skeletonComplete", type="away3d.events.AssetEvent")]
  73. /**
  74. * Dispatched when a skeleton pose asset has been constructed from a resource.
  75. *
  76. * @eventType away3d.events.AssetEvent
  77. */
  78. [Event(name="skeletonPoseComplete", type="away3d.events.AssetEvent")]
  79. /**
  80. * Dispatched when a container asset has been constructed from a resource.
  81. *
  82. * @eventType away3d.events.AssetEvent
  83. */
  84. [Event(name="containerComplete", type="away3d.events.AssetEvent")]
  85. /**
  86. * Dispatched when a texture asset has been constructed from a resource.
  87. *
  88. * @eventType away3d.events.AssetEvent
  89. */
  90. [Event(name="textureComplete", type="away3d.events.AssetEvent")]
  91. /**
  92. * Dispatched when a texture projector asset has been constructed from a resource.
  93. *
  94. * @eventType away3d.events.AssetEvent
  95. */
  96. [Event(name="textureProjectorComplete", type="away3d.events.AssetEvent")]
  97. /**
  98. * Dispatched when a material asset has been constructed from a resource.
  99. *
  100. * @eventType away3d.events.AssetEvent
  101. */
  102. [Event(name="materialComplete", type="away3d.events.AssetEvent")]
  103. /**
  104. * Dispatched when a animator asset has been constructed from a resource.
  105. *
  106. * @eventType away3d.events.AssetEvent
  107. */
  108. [Event(name="animatorComplete", type="away3d.events.AssetEvent")]
  109. /**
  110. * Dispatched when an animation set has been constructed from a group of animation state resources.
  111. *
  112. * @eventType away3d.events.AssetEvent
  113. */
  114. [Event(name="animationSetComplete", type="away3d.events.AssetEvent")]
  115. /**
  116. * Dispatched when an animation state has been constructed from a group of animation node resources.
  117. *
  118. * @eventType away3d.events.AssetEvent
  119. */
  120. [Event(name="animationStateComplete", type="away3d.events.AssetEvent")]
  121. /**
  122. * Dispatched when an animation node has been constructed from a resource.
  123. *
  124. * @eventType away3d.events.AssetEvent
  125. */
  126. [Event(name="animationNodeComplete", type="away3d.events.AssetEvent")]
  127. /**
  128. * Dispatched when an animation state transition has been constructed from a group of animation node resources.
  129. *
  130. * @eventType away3d.events.AssetEvent
  131. */
  132. [Event(name="stateTransitionComplete", type="away3d.events.AssetEvent")]
  133. /**
  134. * Dispatched when an light asset has been constructed from a resources.
  135. *
  136. * @eventType away3d.events.AssetEvent
  137. */
  138. [Event(name="lightComplete", type="away3d.events.AssetEvent")]
  139. /**
  140. * Dispatched when an light picker asset has been constructed from a resources.
  141. *
  142. * @eventType away3d.events.AssetEvent
  143. */
  144. [Event(name="lightPickerComplete", type="away3d.events.AssetEvent")]
  145. /**
  146. * Dispatched when an effect method asset has been constructed from a resources.
  147. *
  148. * @eventType away3d.events.AssetEvent
  149. */
  150. [Event(name="effectMethodComplete", type="away3d.events.AssetEvent")]
  151. /**
  152. * Dispatched when an shadow map method asset has been constructed from a resources.
  153. *
  154. * @eventType away3d.events.AssetEvent
  155. */
  156. [Event(name="shadowMapMethodComplete", type="away3d.events.AssetEvent")]
  157. /**
  158. * The SingleFileLoader is used to load a single file, as part of a resource.
  159. *
  160. * While SingleFileLoader can be used directly, e.g. to create a third-party asset
  161. * management system, it's recommended to use any of the classes Loader3D, AssetLoader
  162. * and AssetLibrary instead in most cases.
  163. *
  164. * @see away3d.loading.Loader3D
  165. * @see away3d.loading.AssetLoader
  166. * @see away3d.loading.AssetLibrary
  167. */
  168. public class SingleFileLoader extends EventDispatcher
  169. {
  170. private var _parser:ParserBase;
  171. private var _req:URLRequest;
  172. private var _fileExtension:String;
  173. private var _fileName:String;
  174. private var _loadAsRawData:Boolean;
  175. private var _materialMode:uint;
  176. private var _data:*;
  177. // Image parser only parser that is added by default, to save file size.
  178. private static var _parsers:Vector.<Class> = Vector.<Class>([ ImageParser ]);
  179. /**
  180. * Creates a new SingleFileLoader object.
  181. */
  182. public function SingleFileLoader(materialMode:uint = 0)
  183. {
  184. _materialMode = materialMode;
  185. }
  186. public function get url():String
  187. {
  188. return _req? _req.url : '';
  189. }
  190. public function get data():*
  191. {
  192. return _data;
  193. }
  194. public function get loadAsRawData():Boolean
  195. {
  196. return _loadAsRawData;
  197. }
  198. public static function enableParser(parser:Class):void
  199. {
  200. if (_parsers.indexOf(parser) < 0)
  201. _parsers.push(parser);
  202. }
  203. public static function enableParsers(parsers:Vector.<Class>):void
  204. {
  205. var pc:Class;
  206. for each (pc in parsers)
  207. enableParser(pc);
  208. }
  209. /**
  210. * Load a resource from a file.
  211. *
  212. * @param urlRequest The URLRequest object containing the URL of the object to be loaded.
  213. * @param parser An optional parser object that will translate the loaded data into a usable resource. If not provided, AssetLoader will attempt to auto-detect the file type.
  214. */
  215. public function load(urlRequest:URLRequest, parser:ParserBase = null, loadAsRawData:Boolean = false):void
  216. {
  217. var urlLoader:URLLoader;
  218. var dataFormat:String;
  219. _loadAsRawData = loadAsRawData;
  220. _req = urlRequest;
  221. decomposeFilename(_req.url);
  222. if (_loadAsRawData) {
  223. // Always use binary for raw data loading
  224. dataFormat = URLLoaderDataFormat.BINARY;
  225. } else {
  226. if (parser)
  227. _parser = parser;
  228. if (!_parser)
  229. _parser = getParserFromSuffix();
  230. if (_parser) {
  231. switch (_parser.dataFormat) {
  232. case ParserDataFormat.BINARY:
  233. dataFormat = URLLoaderDataFormat.BINARY;
  234. break;
  235. case ParserDataFormat.PLAIN_TEXT:
  236. dataFormat = URLLoaderDataFormat.TEXT;
  237. break;
  238. }
  239. } else {
  240. // Always use BINARY for unknown file formats. The thorough
  241. // file type check will determine format after load, and if
  242. // binary, a text load will have broken the file data.
  243. dataFormat = URLLoaderDataFormat.BINARY;
  244. }
  245. }
  246. urlLoader = new URLLoader();
  247. urlLoader.dataFormat = dataFormat;
  248. urlLoader.addEventListener(Event.COMPLETE, handleUrlLoaderComplete);
  249. urlLoader.addEventListener(IOErrorEvent.IO_ERROR, handleUrlLoaderError);
  250. urlLoader.load(urlRequest);
  251. }
  252. /**
  253. * Loads a resource from already loaded data.
  254. * @param data The data to be parsed. Depending on the parser type, this can be a ByteArray, String or XML.
  255. * @param uri The identifier (url or id) of the object to be loaded, mainly used for resource management.
  256. * @param parser An optional parser object that will translate the data into a usable resource. If not provided, AssetLoader will attempt to auto-detect the file type.
  257. */
  258. public function parseData(data:*, parser:ParserBase = null, req:URLRequest = null):void
  259. {
  260. if (data is Class)
  261. data = new data();
  262. if (parser)
  263. _parser = parser;
  264. _req = req;
  265. parse(data);
  266. }
  267. /**
  268. * A reference to the parser that will translate the loaded data into a usable resource.
  269. */
  270. public function get parser():ParserBase
  271. {
  272. return _parser;
  273. }
  274. /**
  275. * A list of dependencies that need to be loaded and resolved for the loaded object.
  276. */
  277. public function get dependencies():Vector.<ResourceDependency>
  278. {
  279. return _parser? _parser.dependencies : new Vector.<ResourceDependency>;
  280. }
  281. /**
  282. * Splits a url string into base and extension.
  283. * @param url The url to be decomposed.
  284. */
  285. private function decomposeFilename(url:String):void
  286. {
  287. // Get rid of query string if any and extract suffix
  288. var base:String = (url.indexOf('?') > 0)? url.split('?')[0] : url;
  289. var i:int = base.lastIndexOf('.');
  290. _fileExtension = base.substr(i + 1).toLowerCase();
  291. _fileName = base.substr(0, i);
  292. }
  293. /**
  294. * Guesses the parser to be used based on the file extension.
  295. * @return An instance of the guessed parser.
  296. */
  297. private function getParserFromSuffix():ParserBase
  298. {
  299. var len:uint = _parsers.length;
  300. // go in reverse order to allow application override of default parser added in Away3D proper
  301. for (var i:int = len - 1; i >= 0; i--) {
  302. if (_parsers[i].supportsType(_fileExtension))
  303. return new _parsers[i]();
  304. }
  305. return null;
  306. }
  307. /**
  308. * Guesses the parser to be used based on the file contents.
  309. * @param data The data to be parsed.
  310. * @param uri The url or id of the object to be parsed.
  311. * @return An instance of the guessed parser.
  312. */
  313. private function getParserFromData(data:*):ParserBase
  314. {
  315. var len:uint = _parsers.length;
  316. // go in reverse order to allow application override of default parser added in Away3D proper
  317. for (var i:int = len - 1; i >= 0; i--) {
  318. if (_parsers[i].supportsData(data))
  319. return new _parsers[i]();
  320. }
  321. return null;
  322. }
  323. /**
  324. * Cleanups
  325. */
  326. private function removeListeners(urlLoader:URLLoader):void
  327. {
  328. urlLoader.removeEventListener(Event.COMPLETE, handleUrlLoaderComplete);
  329. urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, handleUrlLoaderError);
  330. }
  331. /**
  332. * Called when loading of a file has failed
  333. */
  334. private function handleUrlLoaderError(event:IOErrorEvent):void
  335. {
  336. var urlLoader:URLLoader = URLLoader(event.currentTarget);
  337. removeListeners(urlLoader);
  338. if (hasEventListener(LoaderEvent.LOAD_ERROR))
  339. dispatchEvent(new LoaderEvent(LoaderEvent.LOAD_ERROR, _req.url, true, event.text));
  340. }
  341. /**
  342. * Called when loading of a file is complete
  343. */
  344. private function handleUrlLoaderComplete(event:Event):void
  345. {
  346. var urlLoader:URLLoader = URLLoader(event.currentTarget);
  347. removeListeners(urlLoader);
  348. _data = urlLoader.data;
  349. if (_loadAsRawData) {
  350. // No need to parse this data, which should be returned as is
  351. dispatchEvent(new LoaderEvent(LoaderEvent.DEPENDENCY_COMPLETE));
  352. } else
  353. parse(_data);
  354. }
  355. /**
  356. * Initiates parsing of the loaded data.
  357. * @param data The data to be parsed.
  358. */
  359. private function parse(data:*):void
  360. {
  361. // If no parser has been defined, try to find one by letting
  362. // all plugged in parsers inspect the actual data.
  363. if (!_parser)
  364. _parser = getParserFromData(data);
  365. if (_parser) {
  366. _parser.addEventListener(ParserEvent.READY_FOR_DEPENDENCIES, onReadyForDependencies);
  367. _parser.addEventListener(ParserEvent.PARSE_ERROR, onParseError);
  368. _parser.addEventListener(ParserEvent.PARSE_COMPLETE, onParseComplete);
  369. _parser.addEventListener(AssetEvent.TEXTURE_SIZE_ERROR, onTextureSizeError);
  370. _parser.addEventListener(AssetEvent.ASSET_COMPLETE, onAssetComplete);
  371. _parser.addEventListener(AssetEvent.ANIMATION_SET_COMPLETE, onAssetComplete);
  372. _parser.addEventListener(AssetEvent.ANIMATION_STATE_COMPLETE, onAssetComplete);
  373. _parser.addEventListener(AssetEvent.ANIMATION_NODE_COMPLETE, onAssetComplete);
  374. _parser.addEventListener(AssetEvent.STATE_TRANSITION_COMPLETE, onAssetComplete);
  375. _parser.addEventListener(AssetEvent.TEXTURE_COMPLETE, onAssetComplete);
  376. _parser.addEventListener(AssetEvent.CONTAINER_COMPLETE, onAssetComplete);
  377. _parser.addEventListener(AssetEvent.GEOMETRY_COMPLETE, onAssetComplete);
  378. _parser.addEventListener(AssetEvent.MATERIAL_COMPLETE, onAssetComplete);
  379. _parser.addEventListener(AssetEvent.MESH_COMPLETE, onAssetComplete);
  380. _parser.addEventListener(AssetEvent.ENTITY_COMPLETE, onAssetComplete);
  381. _parser.addEventListener(AssetEvent.SKELETON_COMPLETE, onAssetComplete);
  382. _parser.addEventListener(AssetEvent.SKELETON_POSE_COMPLETE, onAssetComplete);
  383. if (_req && _req.url)
  384. _parser._fileName = _req.url;
  385. _parser.materialMode = _materialMode;
  386. _parser.parseAsync(data);
  387. } else {
  388. var msg:String = "No parser defined. To enable all parsers for auto-detection, use Parsers.enableAllBundled()";
  389. if (hasEventListener(LoaderEvent.LOAD_ERROR))
  390. this.dispatchEvent(new LoaderEvent(LoaderEvent.LOAD_ERROR, "", true, msg));
  391. else
  392. throw new Error(msg);
  393. }
  394. }
  395. private function onParseError(event:ParserEvent):void
  396. {
  397. if (hasEventListener(ParserEvent.PARSE_ERROR))
  398. dispatchEvent(event.clone());
  399. }
  400. private function onReadyForDependencies(event:ParserEvent):void
  401. {
  402. dispatchEvent(event.clone());
  403. }
  404. private function onAssetComplete(event:AssetEvent):void
  405. {
  406. this.dispatchEvent(event.clone());
  407. }
  408. private function onTextureSizeError(event:AssetEvent):void
  409. {
  410. this.dispatchEvent(event.clone());
  411. }
  412. /**
  413. * Called when parsing is complete.
  414. */
  415. private function onParseComplete(event:ParserEvent):void
  416. {
  417. this.dispatchEvent(new LoaderEvent(LoaderEvent.DEPENDENCY_COMPLETE, this.url)); //dispatch in front of removing listeners to allow any remaining asset events to propagate
  418. _parser.removeEventListener(ParserEvent.READY_FOR_DEPENDENCIES, onReadyForDependencies);
  419. _parser.removeEventListener(ParserEvent.PARSE_COMPLETE, onParseComplete);
  420. _parser.removeEventListener(ParserEvent.PARSE_ERROR, onParseError);
  421. _parser.removeEventListener(AssetEvent.TEXTURE_SIZE_ERROR, onTextureSizeError);
  422. _parser.removeEventListener(AssetEvent.ASSET_COMPLETE, onAssetComplete);
  423. _parser.removeEventListener(AssetEvent.ANIMATION_SET_COMPLETE, onAssetComplete);
  424. _parser.removeEventListener(AssetEvent.ANIMATION_STATE_COMPLETE, onAssetComplete);
  425. _parser.removeEventListener(AssetEvent.ANIMATION_NODE_COMPLETE, onAssetComplete);
  426. _parser.removeEventListener(AssetEvent.STATE_TRANSITION_COMPLETE, onAssetComplete);
  427. _parser.removeEventListener(AssetEvent.TEXTURE_COMPLETE, onAssetComplete);
  428. _parser.removeEventListener(AssetEvent.CONTAINER_COMPLETE, onAssetComplete);
  429. _parser.removeEventListener(AssetEvent.GEOMETRY_COMPLETE, onAssetComplete);
  430. _parser.removeEventListener(AssetEvent.MATERIAL_COMPLETE, onAssetComplete);
  431. _parser.removeEventListener(AssetEvent.MESH_COMPLETE, onAssetComplete);
  432. _parser.removeEventListener(AssetEvent.ENTITY_COMPLETE, onAssetComplete);
  433. _parser.removeEventListener(AssetEvent.SKELETON_COMPLETE, onAssetComplete);
  434. _parser.removeEventListener(AssetEvent.SKELETON_POSE_COMPLETE, onAssetComplete);
  435. }
  436. }
  437. }