How To Use A DisplayObject As A Dynamic Texture
In this tutorial we will learn how to use any DisplayObject as a dynamic texture. DisplayObject is the root class for Flash' display list. Therefore, this tutorial applies to anything that Flash can display using the classic display list, including - but not limited to - the following classes: Sprite, Shape, Textfield, Video.
In order to do this, we will learn how to use the DynamicTextureController.
Minko provides a custom controller to do exactly this: rasterize a DisplayObject "at every frame" and upload it to a texture. This controller is called the DynamicTextureController. The constructor of this controller expects at least the 3 following arguments:
- source : DisplayObject, the DisplayObject that shall be rasterized and uploaded as a texture at every frame
- width : Number, the width of the "animated" texture
- height : Number, the height of the "animated" texture
Optionnal constructor arguments
- mipMapping : Boolean, whether the animated texture should use mipmapping or not, the default value is true
- framerate : Number, a custom framerate value to have a different framerate for the animated texture and the application, the default value is 30
- propertyName : String, the name of property that will be added to the target binding, the default value is 'diffuseMap'
- matrix : Matrix, a Matrix object that defines a custom 2D transformation to apply to the source DisplayObject upon rasterization, the default value is null
- forceBitmapDataClear : Boolean, whether to clear the BitmapData used for rasterization in order to avoid accumulation side effects when working with a transparent DisplayObject
Once instanciated, the DynamicTextureController is expected to target Mesh or Scene nodes. Anyway, the same DynamicTextureController instance can be used on multiple Mesh or Scene nodes. Actually, if you intend to use the very same animated texture multiple times, you should use the same DynamicTextureController in order to minimize rasterization operations and get better performances.
The DynamicTextureController will not create a texture for each "frame" of the source DisplayObject. Instead, it will create a single and only texture of the specified width and height and will rasterize the source DisplayObject in a BitmapData "at every frame" and upload this BitmapData into the previously allocated texture. If you want to work with an animated texture with a fixed set of frames, you should rather use spritesheets.
In this example, we will simply use an embedded SWF file as a dynamic texture.
Step 0: embedding and instanciate SWF file
The first step is to embed the SWF file using the Embed metatada tag. Of course, it would work with a SWF loaded dynamically or with any other DisplayObject, but we just want to keep things as simple as possible for this example:
[Embed("../assets/minko_logo.swf")] private static const MINKO_LOGO_SWF : Class;
The SWF file we will use for this demo is embedded below:File:Minko logo.swf
Once embedded, our SWF file can be instanciated as a DisplayObject using the 'new' operator:
var minkoLogo : DisplayObject = new MINKO_LOGO_SWF();
Step 1: create the DynamicTextureController
Again: working with an (embedded) SWF file is for demonstration purposes only and for the sake of simplicity. If your application already provides a DisplayObject reference you might want to use, just use it instead.
This DisplayObject can then be passed as the source argument to create the DynamicTextureController:
var dynamicTextureCtrl : DynamicTextureController = new DynamicTextureController( minkoLogo, 512, 512 );
Step 2: add the DynamicTextureController to a Mesh
When our DynamicTextureController has been successfully created, we just have to add it to the Mesh that shall be rendered using the corresponding animated texture:
You should get the following result:
Step 2 (alternative): add the DynamicTextureController to the Scene
The DynamicTextureController can also be used to have an animated texture in the scene bindings. This is very useful if you need some animated texture available for the whole scene. To do this, you simply have to add the controller to the Scene node itself:
This animated texture will then be available from your custom shaders using the sceneBindings.getTextureParameter() method passing the property name used upon creation of the DynamicTextureController.
Adding the DynamicTextureController to the Scene node directly will not "render" the animated texture: textures cannot be "rendered"; geometry can be rendered and it just might use one or more textures in the process. Instead, adding the DynamicTextureController to the Scene node directly will just add the "animated" texture to the scene bindings, making it possible to use it in any shader using the
The main reason to add textures - or any property - to the scene bindings instead of the mesh bindings (like materials do) is to have this property available from any shader. Such properties are global to the scene instead of rely on specific per-mesh properties like the material properties.
Target custom texture material properties
The DynamicTextureController can be used to do a lot more than just animated diffuse textures. By default, the DynamicTextureController will set a binding property named 'diffuseMap'. But this property name can be passed to the DynamicTextureController in order to target another texture.
For example, the following code sample will create a DynamicTextureController that will target the 'normalMap' property:
mesh.addController(new DynamicTextureController( sourceDisplayObject 512, 512, false, 24, 'normalMap' ));
About data binding
The DynamicTextureController will not modify the corresponding texture property of the material of the target Mesh. Instead, it will create a new DataProvider with a single property to hold the texture. Therefore, if your DynamicTextureController is set to work on the 'diffuseMap' property, you must make sure that this property does not exist in the data bindings of the Mesh already. Otherwise, you will likely get an error shouting the same binding is already declared by another property.
The following code snippet shows how to remove the 'diffuseMap' property from the Mesh material before adding the DynamicTextureController that will re-declare it:
// remove the property from the material to avoid duplicated properties in the mesh bindings mesh.material.removeProperty('diffuseMap'); // add the DynamicTextureController that will declare/add a custom data provider for the 'diffuseMap' property mesh.addController(new DynamicTextureController( sourceDisplayObject 512, 512, true, 24, 'diffuseMap' ));
The width and height values passed to the DynamicTextureController will affect the size of the allocated texture on the graphics hardware memory. In order to keep memory usge to a minimum and to save memory bandwidth, you should keep the size of the texture to a minimum. On mobile devices especially, memory bandwidth is quite low and upload operations are slow and should be optimized.
The framerate of the DynamicTextureController will affect how many times per second the source DisplayObject has to be rasterized and uploaded to the graphics memory. Therefore, this value has a direct impact on both the CPU and the graphics memory bandwidth usage. In order to get the best performances, keep this value to a minimum.
By definition, it is pointless to have a DynamicTextureController framerate higher than the actual application framerate.
Working with mip mapping will require to create and upload every mipmap everytime the source DisplayObject is rasterized. Generating mipmaps can be very expensive both in terms of CPU and graphics memory bandwidth usage. Therefore, it is recommended to disable mip mapping.
You must remember that disabled mip mapping on the texture is not enough: the material and the corresponding shaders have to be set not to use mip mapping. To do this, you must set the mip mapping property according to the texture property who are working with.
For example, if your DynamicTextureController targets the 'diffuseMap' property, you will need to change the 'diffuseMapMipMapping' property:
mesh.material.diffuseMapMipMapping = SamplerMipMapping.DISABLE; // add the DynamicTextureController that will declare/add a custom data provider for the 'diffuseMap' property mesh.addController(new DynamicTextureController( sourceDisplayObject 512, 512, false, // disable mip mapping 24, 'diffuseMap' ));