[Flash] Preloading Collada’s textures with Away3D
Well this post has been delayed for such a long time... First, six months ago I moved to Montreal which it is one the greatest thing I've done! But unfortunately just 2 months after, my health went really bad and I had to come back in France for 2 months and half (absolute need of family support)... Patiently took care of me, can't do something else...
Well now I'm back to Montreal, better than ever! Well almost
So it's time to a new blog post
So now if you wanted to know how to add a custom preload to your Collada's textures, you have found a solution here! By default the Collada's parser of Away3d autoload textures that your model need. Which is great, it's an easy way to quickly display your model. But now let see how to preload them and more change material declaration. (Away3d version used: 3.6.0)
-> View the example <-
Example source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <nomatter.me@gmail.com> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return Nicolas Schiro * ---------------------------------------------------------------------------- */ package { import away3d.cameras.Camera3D; import away3d.cameras.TargetCamera3D; import away3d.cameras.lenses.PerspectiveLens; import away3d.containers.ObjectContainer3D; import away3d.containers.View3D; import away3d.core.base.Mesh; import away3d.core.render.Renderer; import away3d.lights.DirectionalLight3D; import away3d.lights.PointLight3D; import away3d.loaders.Collada; import away3d.loaders.data.MaterialData; import away3d.loaders.utils.MaterialLibrary; import away3d.materials.BitmapMaterial; import away3d.materials.ColorMaterial; import away3d.materials.ShadingColorMaterial; import away3d.materials.WhiteShadingBitmapMaterial; import away3d.primitives.Sphere; import br.com.stimuli.loading.BulkLoader; import br.com.stimuli.loading.BulkProgressEvent; import eu.nomatter.away3d.utils.ColladaContentUtils; import eu.nomatter.collada.Cinema4D; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.geom.Vector3D; import flash.net.URLLoader; import flash.net.URLRequest; import flash.utils.Dictionary; [SWF(width='1000', height='624', backgroundColor='#dedede', frameRate='30')] public class SampleColladaContentUtils extends Sprite { private var colladaLoader : URLLoader; private var collada : XML; private var progressTexturesBar : ProgressTexturesBar; private var colladaContentUtils : ColladaContentUtils; private var urlsTextures : Vector.<String> private var texturesLoader : BulkLoader; private var view : View3D; private var model : ObjectContainer3D; public function SampleColladaContentUtils() { // Load Collada this.colladaLoader = new URLLoader(); this.colladaLoader.addEventListener(Event.COMPLETE, this.completeColladaLoading ); this.colladaLoader.load( new URLRequest( "assets/nomatter_bake.dae" ) ); } private function completeColladaLoading( event : Event ) : void { // Get Collada this.collada = XML( this.colladaLoader.data ); this.colladaLoader.removeEventListener(Event.COMPLETE, this.completeColladaLoading ); this.colladaLoader = null; this.colladaContentUtils = new ColladaContentUtils(); this.urlsTextures = this.colladaContentUtils.getTextures( collada ); // ProgressBar this.progressTexturesBar = new ProgressTexturesBar(); this.progressTexturesBar.addEventListener(Event.COMPLETE, this.texturesComplete ); this.addChild( this.progressTexturesBar ); // Load textures this.texturesLoader = new BulkLoader( "TEXTURES_LOADER" ); for each( var url : String in urlsTextures ) { this.texturesLoader.add( "assets/"+url, {id:url} ); } this.texturesLoader.addEventListener( BulkProgressEvent.PROGRESS, this.texturesProgress ); this.texturesLoader.start( 3 ); } private function texturesProgress( event : BulkProgressEvent ) : void { this.progressTexturesBar.progress( event.percentLoaded ); } private function texturesComplete( event : Event ) : void { // Remove progressBar this.progressTexturesBar.removeEventListener( Event.COMPLETE, this.texturesComplete ); this.removeChild( this.progressTexturesBar ); this.progressTexturesBar = null; // get Texture var textures : Dictionary = new Dictionary( true ); for each( var id:String in urlsTextures ) { textures[ id ] = this.texturesLoader.getBitmapData( id, false ); } // Get the 3D model this.model = Collada.parse( this.collada, {autoLoadTextures:false} ); // Here it is! Add materials var ml : MaterialLibrary = this.model.materialLibrary; var m : MaterialData; var bitmap : BitmapData; for each( m in ml ) { bitmap = textures[ m.textureFileName ]; if( m.textureFileName == "floor.jpg" ) { m.material = new BitmapMaterial( bitmap ); } else { m.material = new WhiteShadingBitmapMaterial( bitmap, {smooth:true} ); } } // Recover cameras var cameras : Dictionary = this.colladaContentUtils.getCameras( this.collada ); var camera : Camera3D = cameras["cam"] as Camera3D; camera.lens = new PerspectiveLens(); camera.moveBackward( 1000 ); // View 3D this.view = new View3D( {x:this.stage.stageWidth/2, y:this.stage.stageHeight/2, camera:camera, renderer:Renderer.CORRECT_Z_ORDER } ); this.view.scene.addChild( this.model ); this.addChild( this.view ); var light : DirectionalLight3D = new DirectionalLight3D( {direction:new Vector3D(500,-300,200) } ); this.view.scene.addLight( light ); // Clear loader this.texturesLoader.clear(); this.texturesLoader = null; // Render textures = null; this.addEventListener( Event.ENTER_FRAME, this.render ); } private function render( event : Event ) : void { this.model.rotationY++; this.view.render(); } } } |
I've simply written a class that parse the collada's data and return all urls textures.
This class can get camera's as well (just Camera3D no TargetCamera3D)
ColladaContentUtils.as source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <nomatter.me@gmail.com> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return Nicolas Schiro * ---------------------------------------------------------------------------- */ package eu.nomatter.away3d.utils { import away3d.cameras.Camera3D; import away3d.core.base.Object3D; import flash.display.*; import flash.events.Event; import flash.events.ProgressEvent; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.net.URLLoader; import flash.net.URLRequest; import flash.utils.Dictionary; public class ColladaContentUtils { private static var numLoader:int = -1; private var texturesId:Dictionary; public function ColladaContentUtils() { this.texturesId = new Dictionary(); } public function getTextures( collada : XML ) : Vector.<String> { var colladaNoNamespace : XML = new XML( collada.toXMLString().replace(/\s+xmlns(:[^=]+)?="[^"]*"/g, "") ); var ids : Vector.<String> = new Vector.<String>(); var url:String; for each( var image:XML in colladaNoNamespace.library_images.image ) { url = image.init_from.toString(); if( Boolean( url ) ) { ids.push( url ); } } return ids; } // Camera Utils public function getCameras( collada : XML ) : Dictionary { var colladaNoNamespace : XML = new XML( collada.toXMLString().replace(/\s+xmlns(:[^=]+)?="[^"]*"/g, "") ); var cameras : Dictionary = new Dictionary; var camera:Camera3D; var id:String; var name:String; for each( var cameraXML:XML in colladaNoNamespace.library_cameras.camera ) { id = cameraXML.@id.toString(); name = cameraXML.@name.toString(); camera = new Camera3D(); camera.name = name; camera.transform = this.getCameraTransform( id, colladaNoNamespace ); cameras[ name ] = camera; } return cameras; } private function getCameraTransform( id:String, colladaNoNamespace : XML ) : Matrix3D { for each( var node:XML in colladaNoNamespace.library_visual_scenes.visual_scene.node ) { var result:XML = this.matchCameraId( node, id ); if( Boolean( result ) ) { var translate:Array = result.translate.toString().split( " " ); var rotationX:Number = Number( result.rotate.( @sid == "rotateX" ).toString().split( " " )[ 3 ] ); var rotationY:Number = Number( result.rotate.( @sid == "rotateY" ).toString().split( " " )[ 3 ] ); var rotationZ:Number = Number( result.rotate.( @sid == "rotateZ" ).toString().split( " " )[ 3 ] ); var object:Object3D = new Object3D(); object.rotateTo( rotationX, -rotationY+180, rotationZ ); object.position = new Vector3D( -translate[ 0 ], translate[ 1 ], translate[ 2 ] ); return object.transform; } } return null; } private function matchCameraId( rootNode:XML, id:String ) : XML { var idCamera:String = rootNode.instance_camera.@url.toString().split( "#" )[ 1 ]; if( Boolean( idCamera ) ) { if( id == idCamera ) { return rootNode; } } else if( rootNode.node.length() > 0 ) { for each( var node:XML in rootNode.node ) { var returnNode:XML = this.matchCameraId( node, id ); if( Boolean( returnNode ) ) { return returnNode; } } } return null; } } } |
Download ColladaContentUtils
Download Preloading Collada's Texture project
In a few month "MoleHill" will be released in the FP11 and this will be certainly totally obsolete!
[Website] naturebynoah, Sangue Bom & Vertbaudet
Not 1, Not 2 but 3 websites !
I'v been very busy at work during last months and get no time to update my blog... I have to improve my planning!

Naturebynoah
Present the "Ecoute ta nature" initiative! (not the dharma...)
The first part of the website present the new eco-products made by Bougeois with the participation of Yannick Noah.
The second part is dedicated to our daily contribution to the nature!
Well that's a french website but feel free to post in English! We'll appreciate it ![]()
Sangue Bom
I've just made the sound visualization on the website of Sangue Bom ( but it look great! ), a really great sound designer. If you have a look, you'll notice that he worked on "Cisco : c'est vous le boss" and "Ecoute ta nature" too.
- Creation & Animation: Jocker
- Sound Design: Sangue Bom!
- Developement: _nomatter
Vertbaudet : Mother's Day
Young mom, choose YOUR mother's day! Well mother's day is passed now... But you can still have a look on the final result!
- Conception: Visual-Link
- Creation & 3D: Jocker
- Developement: _nomatter
More to come!
Cinema4D Collada Corrector for Away3D and Papervision3D
---- EDIT : 15 may 2011
Cinema4D.as has been updated, bug fix : script added material property on objects that doesn't need it, cause Away3d parser to crash
AIR Application : Bug Fix, now REALLY update the file... (thanks to Tobias Richter! )
----
Welcome on my blog! You're reading my first post congratulations!
Well what's the subject? Oh year a "Cinema4D Collada corrector" for Away3D and Papervision3D...
On my last project I've got an issue when I tried to integrate a 3D model with several objects AND textures in Away3D.
It seems that, when coming from C4D, Away3D and P3D parser's take the same texture for all meshes. Even if I'm really not an expert in Collada, I'd searched throw the collada's file and parser what happened.
After some headaches to understand (a bit) how a Collada file is constructed (love the node's node's node's imbrication, and some id that "bind" textures to effects, effects to materials, materials with instances, etc. I'm sure it's possible to make a map with warzone and traps based on the collada structure...), I've found that Cinema4D export always the same material's name (a wonderful "Material 1"...) in several nodes.
I've written a class to correct this problem. Now you have Material 1 AND Material 2, Material 3, etc. Not sexy nor explicit but that's working.
Here the code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | package eu.nomatter.collada { public class Cinema4D { private static var materialCount:int = 0; public function Cinema4D() { } public static function correctMaterial( xml:XML ):XML { // Fancy namespace remove http://www.stevensacks.net/2008/07/02/parsing-xhtml-with-e4x-in-as3/ var colladaNamespace:Namespace = xml.namespace(); xml = new XML( xml.toXMLString().replace(/\s+xmlns(:[^=]+)?="[^"]*"/g, "") ); var idGeom:String; Cinema4D.materialCount = 0; var newName:String; var count:int = 0; var nodeGeometry:XMLList; for each( var geomXML:XML in xml.library_visual_scenes.visual_scene.node ) { Cinema4D.correctNode( xml, geomXML ); } xml.@xmlns = colladaNamespace.toString(); return xml; } private static function correctNode( xml:XML, rootNode:XML ):void { var materialName:String = "Material"; if( rootNode.node.length() > 0 ) { for each( var node:XML in rootNode.node ) { Cinema4D.correctNode( xml, node ); } } else { var idGeom:String = String( rootNode.instance_geometry.@url.toString().split("#")[1] ); var newName:String = materialName+String( ++Cinema4D.materialCount ); rootNode.instance_geometry.bind_material.technique_common.instance_material.@symbol = newName; var nodeGeometry:XMLList = xml.library_geometries.geometry.(@id == idGeom); nodeGeometry.mesh.triangles.@material = newName; } } } } |
You can see a very simple test here
Originally I've made an AIR application to patch the file instead of executing the code at runtime. Now I've extract the code which correct the xml in a separate class. In the Air app you can drag and drop your file on the application to correct it or use the "Select file" button.
Oh and if you're a Cinem4D user, let me know if you had experiment the same problem!
AS Class : Cinema4D Collada Corrector AS
AIR File : C4DColladaCorrector AIR install
AIR Project source : C4DColladaCorrector AIR project

