[FLASH] Préchargement des textures d’un Collada avec Away3d
Hé ben je voulais écrire cet article depuis un bon moment... Mais il y a 6 mois maintenant j'ai déménager sur Montréal, ce qui est génial ! Malheureusement à peine 2 mois après je suis tombé très malade et j'ai du revenir en France pour 2 mois et demi (Vraiment besoin d'aide de ma famille). J'ai du prendre soin de ma santé sans pouvoir rien faire d'autre...:( Mais je suis de retour sur Montréal mieux que jamais (ou presque
) Et il est temps d'un nouvel article !
Bon si vous vouliez savoir comment précharger les textures d'un Collada, vous avez une solution juste ici ! Par défaut le parser d'Away3d charge automatiquement les textures d'un Collada, ce qui est excessivement pratique et rapide ! Mais maintenant voyons comment il est possible de faire ça "manuellement" et aussi d'accéder à la déclaration des matériaux. (Version d'Away3d : 3.6.0 )
-> Voir l'exemple <-
Code source de l'exemple :
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(); } } } |
J'ai juste écris une classe qui permet de récupérer les urls des textures contenu dans le fichier collada.
En extra, cette classe permet également de récupérer les caméras (En Camera3D pas de TargetCamera3D)
Code source de ColladaContentUtils.as :
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; } } } |
Télécharger de ColladaContentUtils.as
Télécharger de Projet de préchargement des texture d'un Collada
Bon dans quelques mois "Molehill" serait intégré à la sortie du FP11 et tout ça serait certainement totalement obsolète !
[Website] natureByNoah, Sangue Bom et Vertbaudet
Pas 1, ni 2 mais 3 sites !
J'ai vraiment été totalement dépassé par le temps que m'as pris mes derniers projets... Va falloir que je m'organise un peu mieux !
Sinon une petite news sur les derniers projets sur lesquels j'ai travaillé.

Naturebynoah
L'initiative "Ecoute ta nature" !
La première partie du site présente l'initiative (je vous laisse lire le site) entrepris par Bourgeois en partenariat avec Yannick Noah. L'autre est une partie communautaire dédié à nos gestes quotidiens pour l'environnement. On peut y trouver des astuces intéressantes ! Je vous invite donc (forcément) à regarder tout cela, voir y laisser une petite initiative ![]()
Sangue Bom
Si vous ne connaissez pas encore Sangue Bom, c'est le moment de le découvrir ! Sound Designer talentueux (ce n'est que mon avis bien sur
), on se lasse pas d'apprécier son travail et ses ambiances (d'ailleurs j'ai mis en fond le site pour avoir la musique pendant que j'écris ce post !)
- Creation & Animation: Jocker
- Sound Design: Sangue Bom!
- Developement: _nomatter
Vertbaudet : Fêtes des mères
Jeunes mamans venez choisir VOTRE jour de fête des mères ! Bon je sais c'est déjà passé... Mais bon vous pouvez voir le résultat final !
(En même temps des jeunes mamans qui se ballade sur ce site et qui, en plus, ne connaitrais pas Vertbaudet... Enfin tout est possible il parait... )
- Conception: Visual-Link
- Creation & 3D: Jocker
- Developement: _nomatter
C'est tout pour le moment !
Correcteur de Collada venant de Cinema4D pour Away3D et Papervision3D
---- MODIFICATION : 15 mai 2011
Cinema4D.as Correction du bogue : le script ajoutait une propriété material à des objets qui n'en n'avais pas besoin.
AIR Application : Mis à jour, maintenant met à jour VRAIMENT votre fichier... (merci à Tobias Richter !)
----
Ben le voila enfin ce blog !
Alors c'est quoi le sujet ? Ah moui un "correcteur de Collada" venant de Cinema4D pour Away3 et Papervision3D.
Désolé j'ai pas trouvé de titre plus long...
Sur le dernier projet sur lequel j'ai travaillé (visible ici) j'ai rencontré un problème comme rarement je les aime ! En intégrant le modèle 3d exporté en Collada depuis Cinema4D, il y a eu comme qui dirait un petit souci. Il m'appliquait une seule des textures du fichier sur tous les objets... J'ai essayé avec Away3D et Papervision3D, même sanction.
Malheureusement ma connaissance du format Collada se résumait un peu au bouton export sous Cinema4D...
Est venu le temps de l'exploration (c'est vraiment le mot) de la structure du Collada. J'ai pas été déçu de l'initiative.... Des imbrications dans tous les sens, des propriétés qui font correspondre les textures aux effets, les effets aux matériaux, les matériaux aux instances, etc.
Au final j'ai trouvé ce qui posait problème. Cinema4D met le même nom pour les matériaux dans plusieurs nœuds. Ouais quand on voit écrit "Material 1" partout c'est pas trop dur de deviner que c'est, peut-être, potentiellement, à tout hasard, le problème recherché.
Bref j'ai écris un petite classe pour corriger ça. Maintenant on a Material 1 ET Material 2, Material 3, etc. C'est pas vraiment funky comme nom mais ça permet d'éviter d'avoir sa texture de plancher appliquer sur les murs et les bureaux...
Le code en question :
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; } } } } |
À la base j'avais écris une application AIR pour directement corriger le fichier. Après j'ai extrait le code pour le mettre dans une classe, c'est un petit long de corriger le fichier à chaque fois qu'il y a une modif dessus.
Dans le AIR vous pouvez faire un glisser/déposer dessus pour corriger directement le fichier ou utiliser le bouton "Select file".
Si jamais vous utiliser cinema4d, dites-moi si vous avez rencontré le même problème que me sente moins seul ! J'ai bien essayé de chercher sur différents forums, mais les problèmes liés au Collada sont si nombreux que trouver quelque chose est devenu un enfer...
Le fichier AS : Cinema4D Collada Corrector AS
L'app AIR : C4DColladaCorrector AIR install
Les sources du projet AIR :C4DColladaCorrector AIR project

