ATF Textures ((Adobe Texture Format) have been introduced by Adobe to deal with VRAM.
Why ?
Because GPU can handle two kinds of textures : compressed and uncompressed.
Off course compressed texture seems good but requires several steps to be used.
And that’s what we are going to see.
Model made by Silviuq12
( 135 938 triangles )
Sorry, either Adobe flash is not installed or you do not have it enabled
First of all,
I strongly recommand you to read theses articles made by Jackson Dunstan, as most of what I know about ATF Textures comes from there,
and I don’t want to copy/past what he has wrotten :
- Compressed Stage3D Textures With ATF,
- ATF Texture Compression Image Quality and File Size Samples,
- Compress ATF Textures By 95%
- Using ATF Is Harder Than You Might Think
Now you’ve read these articles you may not need to read the following
Anyway….
What you will need to use ATF :
- the ATF tools (as part of the Game Developer Tools) to generate the ATF Textures : here
- target the flash player 11.4.0
- and get the newest AGALMiniAssembler Class : here
In my exemple I will use my package so you will also need the lastest version (in the sidebar menu).
The first thing to do is to generate the ATF file.
« each platform has different support for compressed textures depending on the hardware chipset being used. »,
and in facts the ATF file can contains three formats :
- DXT for Windows, Mac OS X, and some Android devices (DXT1 for non alpha, DXT5 for alpha).
- ETC1 for other Android devices.
- PVRTC for iOS.
We can choose one, two or the three to be embeded in the ATF file.
As I’m only targeting web I will go for the DXT format.
We also have to decide to use mipmapping or not. (me I will)
So here is the command line to generate the ATF file (png2atf is part of the ATF tools given by Adobe) :
png2atf -c d -i image.png -o image_dxt.atf
Once this done in order to gain space I’ve compressed the ATF file using lzma algorithm.
In the project bin directory you will find the original image files (RATBODY.png and RATHEAD.png), the ATF generated (RATBODY.atf and RATHEAD.atf) and the compressed files (RATBODY.cmp and RATHEAD.cmp).
Let’s use them !
Rather than embeded the compressed files I preferred to define them in a xml file. I find this way more convenient if I want to change them later or to add more textures.
Main.as
The xml is loaded in the init() method :
private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); var loader:URLLoader = new URLLoader; var adresse:URLRequest = new URLRequest("xml/textures.xml"); loader.addEventListener(Event.COMPLETE,xmlTexturesLoaded); loader.addEventListener(ProgressEvent.PROGRESS,onProgress); loader.addEventListener(IOErrorEvent.IO_ERROR,onError); loader.addEventListener(ErrorEvent.ERROR, onError); loader.load(adresse); }
Then we load the textures using a sequential command implementation based on the Command design pattern.
This implementation is basic but does its job for this project.
private function xmlTexturesLoaded(e:Event):void { var xmlTextures:XML = new XML(e.target.data); var cmds:SequentialCommand = new SequentialCommand(); var tObj:Object; var i:int = 0; _texturesTab = new Vector.<Object>(xmlTextures.textures.texture.length(),true); for each (var texture:XML in xmlTextures.textures.texture) { tObj = { id:texture.@id.toString(),width:int(texture.@width),height:texture.@height }; _texturesTab[i]=tObj; var cmd:FileLoaderCmd = new FileLoaderCmd(texture.@file.toString()); cmds.add(cmd); i++; } cmds.addEventListener(CommandEvent.COMPLETE, texturesLoaded); cmds.execute(); }
In the lines 62 and 63 the width and height of the texture image are stored in a Vector , as one of the downside of the ATF Texture file is that they are opaque and there’s no attribute which can be used to know their size. (one other reason for using xml)
Once all the textures loaded we add them to the stored data Vector and pass them (after having uncompressed them) to the World class which will do the render job.
private function texturesLoaded(e:CommandEvent):void { var textureFiles:Array = e.data as Array; for (var i:int; i < textureFiles.length; i++) { var textureBA:ByteArray = textureFiles[i] as ByteArray; textureBA.uncompress(CompressionAlgorithm.LZMA); _texturesTab[i].atfFile = textureBA; } _world = new World(stage,_texturesTab); addChild(_world); }
World.as
This class is almost the same that I used in previous articles so you can follow this link for details.
The important thing is that we pass the Vector containing the textures and textures datas to the mesh class :
private function setMeshs():void { var mesh:RatMesh = new RatMesh(_context,_texturesTab); mesh.modelMatrix.appendRotation(140, Vector3D.Y_AXIS); mesh.position = new Vector3D(0, 0, 1000); _meshes.push(mesh); }
RatMesh.as
Again this class is very similar to the class I used in the mutlishader article (link here).
I used a binary model, and for this model I’m using three kinds of shaders.
One simple for the non texture part, a phong shader for the eyes and a simple texture shader for the texture parts.
One important thing about shaders when using ATF Textures : we have to specify what kind of texture we’re using in the fragment shader
In fact we have to say if it’s an alpha texture or not using
- “dxt1” for Context3DTextureFormat.COMPRESSED (whatever the texture format used, DXT, PVRTC, or ETC1)
- “dxt5” for Context3DTextureFormat.COMPRESSED_ALPHA (whatever the texture format used, DXT, PVRTC, or ETC1)
In our case it will be non alpha texture.
You can see it in the MSimpleATFTextureShader.as class I created in line 82 : tex ft3, v3, fs0<2d, linear, miplinear,dxt1>
The last thing to do is to declare the ATF textures.
This is done via the MTextureManager instance :
for each(var texture:Object in texturesTab) { //_textureManager.addTextureFromBitmap(texture.id, texture.atfFile); _textureManager.addTextureFromATF(texture.id, texture.atfFile,texture.width,texture.height); }
MTextureManager.as
The addTextureFromATF() manages ATF textures creation and upload :
public function addTextureFromATF(id:String,textureATF:ByteArray,width:int,height:int):void{ if (!_textures) _textures = new Vector.<MTexture>(); var texture:Texture = _context.createTexture(width, height, Context3DTextureFormat.COMPRESSED, false); texture.uploadCompressedTextureFromByteArray(textureATF,0); var mTexture:MTexture = new MTexture(id, texture); _textures.push(mTexture); }
The important lines are 178 and 179.
When creating texture from ATF we have to use Context3DTextureFormat.COMPRESSED or Context3DTextureFormat.COMPRESSED_ALPHA constant
and uploadCompressedTextureFromByteArray() for uploading the textures.
And that’s it
any questions or comments,
Don’t hesitate.
Hey there, and thanks for such a detailed, specific example! I have a couple questions though… First, can I use the un-compressed .atf files or do I need to use the compressed .cmp versions? And how, exactly, did you compress the .atfs further? Any chance you could detail how the lzma algorithm worked in this case? Thanks again…