Alternativa 3D Series – Tutorial 2 – Adding Material

by Matthew Casperson 2

In part one of this tutorial series you saw how to create a simple Alternativa 3D Flash application. The end result was a spinning cube, drawn with a wireframe material that displayed the edges of the polygons that made up the cube. In this tutorial I will show you how to add a material to the cube to give it a more appealing look.

Requirements

Alternativa comes with three standard material types: the wireframe material, a fill material, and a textured material. As mentioned, the wireframe material (WireMaterial) shows the edges of the polygons that make up a model. The fill material (FillMaterial) displays one flat colour across the entire model, optionally also showing the same wireframe edges that the wireframe material does. Finally the texture material (TextureMaterial) displays a 2D picture as a material on a model.

This demo allows you to apply one of these three material types to the same rotating cube that we created in the first tutorial. In order to do this we need create some buttons and sliders for the user to interact with.

Alternativa2.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
	xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute"
	xmlns:ns1="*"
	width="600"
	height="400"
	color="#FFFFFF"
	backgroundGradientAlphas="[1.0, 1.0]"
	backgroundGradientColors="[#FFFFFF, #C0C0C0]"
	frameRate="100">

	<ns1:EngineManager id="engineManager" x="0" y="0" width="100%" height="100%"/>
	<mx:HSlider x="56" y="147" width="53" id="sliderRed" minimum="0" maximum="255" snapInterval="1" change="{ApplicationManager.Instance.onColorChanged()}"/>
	<mx:Label x="10" y="147" text="Red" color="#000000"/>
	<mx:HSlider x="56" y="199" width="53" id="sliderBlue" minimum="0" maximum="255" snapInterval="1" change="{ApplicationManager.Instance.onColorChanged()}"/>
	<mx:Label x="10" y="199" text="Blue" color="#000000"/>
	<mx:Image x="10" y="10" id="imgAlternativa" source="@Embed(source='../media/alternativa.jpg')"/>
	<mx:Image x="10" y="340" source="@Embed(source='../media/thetechlabs.png')"/>
	<mx:Button x="10" y="50" label="Wireframe" color="#000000" id="btnWireframe" click="{ApplicationManager.Instance.setWireMaterial()}"/>
	<mx:Button x="10" y="80" label="Texture" color="#000000" id="btnTexture" width="88" click="{ApplicationManager.Instance.setTextureMaterial()}"/>
	<mx:Button x="10" y="110" label="Color Fill" color="#000000" width="88" id="btnColorFill" click="{ApplicationManager.Instance.setFillMaterial()}"/>
	<mx:HSlider x="56" y="173" width="53" id="sliderGreen" minimum="0" maximum="255" snapInterval="1" change="{ApplicationManager.Instance.onColorChanged()}"/>
	<mx:Label x="10" y="173" text="Green" color="#000000"/>

</mx:Application>

You’ll  notice that we can add standard Flex GUI controls to our 3D application just like we would any other Flex application. In this case we have added 3 buttons (one for each material type), 3 sliders (to define the red, green and blue colour components of the wireframe and fill materials), and a few labels. The change event for the sliders have been set to trigger the ApplicationManager setTextureMaterial function, and the click event for the buttons trigger either the setWireMaterial, setFillMaterial or setTextureMaterial functions, corresponding to the type of texture that the button show apply to the cube.

ApplicationManager.as

package
{
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.materials.TextureMaterial;
	import alternativa.engine3d.materials.WireMaterial;
	import alternativa.utils.ColorUtils;

	import flash.display.BlendMode;

	import mx.core.Application;

	/**
	 * 	The ApplicationManager holds all program related logic.
	 */
	public class ApplicationManager
	{
		protected static const WIREFRAME:int = 1;
		protected static const TEXTURE:int = 2;
		protected static const FILL:int = 3;
		protected static var instance:ApplicationManager = null;
		protected var currentMaterial:int = 0;
		protected var rotatingBox:RotatingBox = null;

		public static function get Instance():ApplicationManager
		{
			if (instance == null)
				instance = new ApplicationManager();
			return instance;
		}

		public function ApplicationManager()
		{

		}

		public function startupApplicationManager():ApplicationManager
		{
			// create a new instance of the rotating box
			rotatingBox = new RotatingBox().startupRotatingBox();
			// give the box a wireframe material by default
			setWireMaterial();
			// tell the camera to look at the box
			Application.application.engineManager.cameraController.lookAt(rotatingBox.model.coords);

			return this;
		}

		public function setWireMaterial():void
		{
			currentMaterial = WIREFRAME;
			rotatingBox.model.cloneMaterialToAllSurfaces(new WireMaterial(1, getCurrentColor()));
		}

		public function setTextureMaterial():void
		{
			currentMaterial = TEXTURE;
			rotatingBox.model.cloneMaterialToAllSurfaces(new TextureMaterial(ResourceManager.Texture1_Tex));
		}

		public function setFillMaterial():void
		{
			currentMaterial = FILL;
			rotatingBox.model.cloneMaterialToAllSurfaces(new FillMaterial(getCurrentColor(), 1, BlendMode.NORMAL, 2, 0x000000));
		}

		public function onColorChanged():void
		{
			// reapply the current texture, taking into account the new color
			switch (currentMaterial)
			{
				case WIREFRAME:
					setWireMaterial();
					break;
				case FILL:
					setFillMaterial();
					break;
			}
		}

		/**
		 *	Returns a uint reprensenting the color specified by the 3 sliders in the main GUI
		 */
		protected function getCurrentColor():uint
		{
			return ColorUtils.rgb(Application.application.sliderRed.value, Application.application.sliderGreen.value, Application.application.sliderBlue.value);
		}
	}
}

The ApplicationManager holds the bulk of the code that relates to assigning the new material to the cube. We introduce a property called currentMaterial, to which we assign one of three predefined integer constants depending on what type of material we have just assigned to the cube. You can see this in the setWireMaterial, setFillMaterial and etTextureMaterial
functions. It’s also in these functions that we use the cloneMaterialToAllSurfaces function to assign the various material types to the cube.

The onColorChanged function, called when the user modifies one of the color value sliders in the GUI, uses the currentMaterial property to reapply the current material type. You’ll notice that the constructors for both the WireMaterial and FillMaterial reference the getCurrentColor function, which returns a uint representing the color selected by the user with the sliders. By calling the setWireMaterial and setFillMaterial functions again we can create a material with the new color specified.

The TextureMaterial class is a little different. It displays a 2D image as a material. So where do we get this texture from?

ResourceManager.as

package
{
	import alternativa.types.Texture;

	/**
	 * 	ResourceManager is where we embed and create the resources needed by our application
	 */
	public class ResourceManager
	{
		[Embed(source="../media/texture1.jpg")]
		public static const Texture1:Class;
		public static const Texture1_Tex:Texture = new Texture(new Texture1().bitmapData, "Texture1_Tex");
	}
}

For that we introduce a new class called ResourceManager. It’s in the ResourceManager that we create and hold the resources used by the application. Take a look at the line [Embed(source=”../media/texture1.jpg”)]. The ability to embed files is a neat feature of Flex. By using the Embed keyword we can take a separate file on our development machine and store it inside the resulting SWF file that is created by the Flex compiler. This means you only need to distribute the one SWF file instead of maintaining a number of separate files.  In this case we are embedding a JPG image. This embedded file is then used by the Alternativa Texture class, which is then in turn used by the TextureMaterial class to
create a material that can be applied to our cube.

You can see how we have built on top of the first tutorial to quickly and easily create a new Alternativa application. The EngineManager, BaseObject and MeshObject classes have not changed at all. The ApplicationManager has changed quite a bit, but this is what you would expect considering that it is supposed to hold most of the application specific logic.
We have also introduced the ResourceManager class, which gives us a place to embed and hold the various resources used by Alternativa. The ResourceManager will feature in almost every Alternativa tutorial from here on in.

Check out the demo here, and download the source code here.

Comments (2)

  1. thats really great example for alternativa.
    I hope that continues

  2. How does the ResourceManager know to use the immediately above it embedded file? Specifically if I add the following code to ResourceManager I can get a button to access the WaterTex but don’t understand why it works (i.e. I’m not even using the right jpg name):

    [Embed(source=’../media/Water1.JPG’)]
    public static const Water:Class;
    public static const WaterTex:TextureMaterial = new TextureMaterial(new Texture(new Water().bitmapData));

    Furthermore can you confirm that my inference as follows is correct?
    Line1. Embed the file in the swf
    Line2. Create a reference to that file in the code by declaring it as a class
    Line3. Create a TextureMaterial named WaterTex by taking the bitmapData property of the Water class . . . ???? — I don’t get this process even if it works 😉

    Thanks in advance

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>