Media Art, User Experience Design, and Interface Development

Apply a texture on a cube in Away3D 4.0

Posted on 2011-08-20

Away3D cube with a texture applied

Now that your environment is set up to play with Away3D, let’s create something! In my previous post, I concluded that you can now experiment, and so I did too. It turned out that there are some little intricacies about putting a simple image on a simple cube. Let’s take a look.

Create a cube

This part has got to be the simplest, thanks to the nice API that the guys from Away3D built.

ActionScript 3.0:

package
{
import away3d.containers.View3D;
import away3d.materials.ColorMaterial;
import away3d.primitives.Cube;

import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;

[SWF(frameRate="60", backgroundColor="#ffffff", width="640", height="360")]
public class Main extends Sprite
{
	// + ----------------------------------------
	//		[ VARS ]
	// + ----------------------------------------

	private	var	_view3D		:View3D;
	private	var	_cube		:Cube;

	// + ----------------------------------------
	//		[ CONSTRUCTOR ]
	// + ----------------------------------------

	public function Main()
	{
		super();
		initStage();
		addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
	}

	// + ----------------------------------------
	//		[ PRIVATE METHODS ]
	// + ----------------------------------------

	private function initStage():void
	{
		stage.align = StageAlign.TOP_LEFT;
		stage.scaleMode = StageScaleMode.NO_SCALE;
	}

	private function init():void
	{
		// it is necessary to add a texture to the shape so it is visible
		var material:ColorMaterial = new ColorMaterial(0xffffff);

		// I personally prefer to set vars for repetitive values
		var cubeSize:uint = 240;
		_cube = new Cube(material, cubeSize, cubeSize, cubeSize);

		// add the 3D elements to the stage
		_view3D = new View3D();
		_view3D.scene.addChild(_cube);
		addChild(_view3D);

		// add a listener so it becomes possible to render the 3D world
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}

	// + ----------------------------------------
	//		[ PRIVATE METHODS ]
	// + ----------------------------------------

	private function addedToStageHandler(event:Event):void
	{
		removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
		init();
	}

	private function enterFrameHandler(event:Event):void
	{
		// render the 3D world
		_view3D.render();
	}
}

}

With this code, this is what you should get as a result:

Away3D cube with ColorMaterial applied

Not exactly exciting. For one, a ColorMaterial is really a flat color, so the best way to make it live a bit is adding light elements, which we will not see in this post (and that’s also because I have not learned that yet). Also, we only see once face of the cube, quite boring.

To at least view that this object is in 3D, simply update the enterFrameHandler function.

ActionScript 3.0:

private function enterFrameHandler(event:Event):void
{
	// increment the cube's rotation values to see its different sides
	_cube.rotationX++;
	_cube.rotationY++;
	_cube.rotationZ++;

	// render the 3D world
	_view3D.render();
}

Apply an image texture

Most probably, you would like to add your own textures to your 3D elements. This is where it gets fun!

As suggested by a lot of users on the Away3D forums, it may be better to have a 3D artist create objects in Blender or 3ds Max (I do not know if Unity3D can be used, although I hope it can). Once those objects are created, there is a way to import/load them into Away3D.

But in this case, since I do not know 3D software and because there is no need yet to get into complicated texture mapping, we are looking at something super simple: put an image on a cube. Most of what follows is a comprehensive summary of a thread that followed my question on Away3D’s forum.

First things first: let’s create an image. There are specifications to follow when creating an image for a BitmapMaterial.

A texture does not need to be square, however the dimensions have to be a power of two. What this means, is a texture can be 512 x 512 pixels, or 256 x 1024 pixels, etc. I worked with 2048 x 1024 pixels in this case.

Keep in mind that if you image is not square, it will not mipmap. Not fully convinced I understand what it means, as I do now know 3D vocabulary that much, but suffice to say you simply need to set BitmapMaterial.mipmap to false to make it work.

In the case of a cube, you need to cover all six sides. In order to do so, split your image into a 3 x 2 grid, like so:

Dice texture

Do you notice that each part of the image that represents a side of the cube is not square? It does not matter really, since Away3D will map it as a square onto the cube. As long as you respect the grid in your design, it will be good.

Rather than loading the image and in order to make things quicker, I simply embedded it in the code.

ActionScript 3.0:

package
{
import away3d.containers.View3D;
import away3d.materials.BitmapMaterial;
import away3d.primitives.Cube;

import flash.display.Bitmap;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;

[SWF(frameRate="60", backgroundColor="#ffffff", width="640", height="360")]
public class Main extends Sprite
{
	// + ----------------------------------------
	//		[ CONSTANTS ]
	// + ----------------------------------------

	[Embed(source="../assets/images/dice-texture-2048x1024.png")]
	private var ImageClass:Class;

	// + ----------------------------------------
	//		[ VARS ]
	// + ----------------------------------------

	private	var	_view3D		:View3D;
	private	var	_cube		:Cube;

	// + ----------------------------------------
	//		[ CONSTRUCTOR ]
	// + ----------------------------------------

	public function Main()
	{
		super();
		initStage();
		addEventListener(Event.ADDED_TO_STAGE, addedToStageHangler);
	}

	// + ----------------------------------------
	//		[ PRIVATE METHODS ]
	// + ----------------------------------------

	private function initStage():void
	{
		stage.align = StageAlign.TOP_LEFT;
		stage.scaleMode = StageScaleMode.NO_SCALE;
	}

	private function init():void
	{
		// create a Bitmap from the embedded image
		var image:Bitmap = new ImageClass() as Bitmap;

		// create the BitmapMaterial
		var material:BitmapMaterial = new BitmapMaterial(image.bitmapData);
		material.smooth = true;
		// little trick: you do not need to care if the image is square,
		// simply evaluate if the sides are even
		material.mipmap = (image.width == image.height);

		// create the cube
		var cubeSize:uint = 240;
		_cube = new Cube(material, cubeSize, cubeSize, cubeSize);

		// add the 3D elements to the stage
		_view3D = new View3D();
		_view3D.scene.addChild(_cube);
		addChild(_view3D);

		// add a listener so it becomes possible to render the 3D world
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}

	// + ----------------------------------------
	//		[ PRIVATE METHODS ]
	// + ----------------------------------------

	private function addedToStageHandler(event:Event):void
	{
		removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
		init();
	}

	private function enterFrameHandler(event:Event):void
	{
		// increment the cube's rotation values to see its different sides
		_cube.rotationX++;
		_cube.rotationY++;
		_cube.rotationZ++;

		// render the 3D world
		_view3D.render();
	}
}

}

Fix the Away3D sources

Now, I ran into an issue when I originally tested this (Flash Player 11+ needed, click and drag to move the cube):

Cube texture issue

Some sides were repeated or not applied properly. That is actually what prompted me to ask my question on the forum, as I thought I had done everything proper.

I actually did, but here also, there was a correction needed in the Away3D sources. Away3d forum member 80prozent posted a fix.

Look at the function Cube.buildUVs() into the away3d.primitives.Cube class and replace this part:

ActionScript 3.0:

for(i = 0; i <= _segmentsW; i++)
{
	outer_uv = u_tile_step + u_tile_dim * (i / _segmentsW);

	for(j = 0; j <= _segmentsD; j++)
	{
		uvData[uidx++] = outer_uv;
		uvData[uidx++] = 1 - v_tile_dim * (j / _segmentsD);
		uvData[uidx++] = outer_uv;
		uvData[uidx++] = v_tile_step + v_tile_dim * (j / _segmentsD);
	}
}

for(i = 0; i <= _segmentsH; i++)
{
	outer_uv = v_tile_step + v_tile_dim * (i / _segmentsW);

	for(j = 0; j <= _segmentsD; j++)
	{
		uvData[uidx++] = (u_tile_dim * ((_segmentsD - j) / _segmentsD));
		uvData[uidx++] = 1 - outer_uv;
		uvData[uidx++] = 1 - (u_tile_dim * ((_segmentsD - j) / _segmentsD));
		uvData[uidx++] = v_tile_step + v_tile_dim * ((_segmentsW - i) / _segmentsW);
	}
}

target.updateUVData(uvData);

with this part:

ActionScript 3.0:

for(i = 0; i <= _segmentsW; i++)
{
	outer_uv = u_tile_step + u_tile_dim * (i / _segmentsW);

	for(j = 0; j <= _segmentsD; j++)
	{
		uvData[uidx++] = outer_uv;
		uvData[uidx++] = 1 - v_tile_dim * (j / _segmentsD);
		uvData[uidx++] = 1 - outer_uv;
		uvData[uidx++] = 1 - (v_tile_step + v_tile_dim * (j / _segmentsD));
	}
}

for(i = 0; i <= _segmentsH; i++)
{
	outer_uv = v_tile_step + v_tile_dim * (i / _segmentsW);

	for(j = 0; j <= _segmentsD; j++)
	{
		uvData[uidx++] = (u_tile_dim * ((_segmentsD - j) / _segmentsD));
		uvData[uidx++] = 1 - outer_uv;
		uvData[uidx++] = 1 - (u_tile_dim * ((_segmentsD - j) / _segmentsD));
		uvData[uidx++] = v_tile_step + v_tile_dim * ((_segmentsW - i) / _segmentsW);
	}
}

target.updateUVData(uvData);

His fix corrects the way the image is applied to the cube. I guess this will be eventually fixed, but at the time of writing this post, the sources provided by Away3D still need that correction.

And there you go! From then you can have an awesomely decorated cube with the texture of your choice!


3 responses to “Apply a texture on a cube in Away3D 4.0”

  1. […] Janson Blanchet shows how to apply a texture on a cube in Away3D 4.0 including some discussion of difficulties he faced due to a small bug that were resolved through […]

  2. Tom says:

    Nice and simple thanks. Need more samples to get you going on the new Away3D 4.

  3. […] At first, we would need an image, and a plane onto which we would like to place the image. As I wrote previously, materials applied onto shapes need to be in dimensions that are a power of 2, so will will need to […]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.