Erain 3D
advert here

Author: makc
Date: February 24th 2008
version: 3.0.2

Using bitmap tiling in Sandy3D v3.0.2

Objective of the tutorial

In this tutorial you will learn how to use bitmap tiling, new feature of Sandy3D v3.0.2. In order to understand how this tutorial works you should have completed tutorials that teach you how to use simple primitives and how to apply materials to them. Alternatively, you could just watch my video tutorial.

How to

Set up

Create AS3 600×300 FLA, set the document class to V302_Tiling, and paste AS code below in V302_Tiling.as. Create three Death Star textures. Alternatively, grab everything you need in RAR file below:

v302_tiling.rar

The AS Code

package
{
	import flash.display.*
	import flash.events.*

	import sandy.core.*
	import sandy.core.data.*
	import sandy.core.scenegraph.*
	import sandy.events.*
	import sandy.materials.*
	import sandy.materials.attributes.*
	import sandy.primitive.*

	public class V302_Tiling extends Sprite
	{
		private var world:Scene3D = new Scene3D ("scene", this as Sprite, new Camera3D (600, 300), new Group ("root"));
		
		private var dsMat1:BitmapMaterial;
		private var dsMat2:BitmapMaterial;
		private var dsMat3:BitmapMaterial;

		public function V302_Tiling ()
		{
			// create death star materials
			dsMat1 = new BitmapMaterial (new ImageDS1 (1, 1), null, 5);
			dsMat1.repeat = true;
			dsMat1.maxRecurssionDepth = 10;
			
			dsMat2 = new BitmapMaterial (new ImageDS2 (1, 1), null, 5);
			dsMat2.repeat = true;
			dsMat2.maxRecurssionDepth = 8;
			
			dsMat3 = new BitmapMaterial (new ImageDS3 (1, 1), null, 5);
			dsMat3.repeat = true;
			dsMat3.maxRecurssionDepth = 7;

			// create planes
			var pLeft:Plane3D = new Plane3D ("p_left", 10000, 10000, 1, 1, Plane3D.ZX_ALIGNED);
			pLeft.appearance = new Appearance (dsMat1);
			pLeft.enableClipping = true;
			pLeft.x = -5000 -200; pLeft.y = -200; pLeft.z = +4000;

			var pRiht:Plane3D = new Plane3D ("p_riht", 10000, 10000, 1, 1, Plane3D.ZX_ALIGNED);
			pRiht.appearance = new Appearance (dsMat1);
			pRiht.enableClipping = true;
			pRiht.x = +5000 +200; pRiht.y = -200; pRiht.z = +4000;
			
			var pSidL:Plane3D = new Plane3D ("p_sidL", 10000, 400, 1, 1, Plane3D.YZ_ALIGNED);
			pSidL.appearance = new Appearance (dsMat2);
			pSidL.enableClipping = true;
			pSidL.x = -200; pSidL.y = -400; pSidL.z = +4000;

			// we are actually going to be looking at plane backface
			pSidL.enableBackFaceCulling = false;

			var pSidR:Plane3D = new Plane3D ("p_sidR", 10000, 400, 1, 1, Plane3D.YZ_ALIGNED);
			pSidR.appearance = new Appearance (dsMat2);
			pSidR.enableClipping = true;
			pSidR.x = +200; pSidR.y = -400; pSidR.z = +4000;

			var pBott:Plane3D = new Plane3D ("p_bott", 10000, 400, 1, 1, Plane3D.ZX_ALIGNED);
			pBott.appearance = new Appearance (dsMat3);
			pBott.enableClipping = true;
			pBott.y = -600; pBott.z = +4000;

			// add them to scene
			var scene:Group = new Group ("root");

			scene.addChild (pLeft);
			scene.addChild (pRiht);
			scene.addChild (pSidL);
			scene.addChild (pSidR);
			scene.addChild (pBott);

			scene.addChild (world.camera);

			world.root = scene;

			addEventListener (Event.ENTER_FRAME, onEnterFrame);
			
			addEventListener (Event.ADDED_TO_STAGE, onAddedToStage);
		}
		
		private function onAddedToStage (e:Event):void
		{
			// needed to keep reasonable fps
			stage.quality = StageQuality.LOW; removeEventListener (Event.ADDED_TO_STAGE, onAddedToStage);
		}

		private var offset:Number = 0;

		private function onEnterFrame (e:Event):void
		{
			dsMat1.setTiling (20, 20, 0, -2 * offset);
			dsMat2.setTiling ( 1, 20, 0, -2 * offset);
			dsMat3.setTiling ( 1, 10, 0, -1 * offset);

			// add some interactivity
			offset += 2e-2 + 2e-4 * (300 - mouseY);
			if (offset > 1) offset--;
			world.camera.x = mouseX - 300;
			world.camera.y = mouseY;

			world.camera.lookAt (0, -200, 0);
			world.render ();
		}
	}
}

Step by step

In this tutorial, we will simulate a flight over Death Star surface. So the first thing to do is to build a model of the surface. From the quick analythis of the scene, we can see that we are going to need at least 5 planes:

deathstarcanyon.jpg

We can use same texture for two surface planes, and a bit darker textures for canyon sides and floor. To cover the whole screen, our planes are going to be very large, and so to avoid perspective distortions, we have to increase recursion level cap on materials:

// create death star materials
dsMat1 = new BitmapMaterial (new ImageDS1 (1, 1), null, 5);
dsMat1.repeat = true; dsMat1.maxRecurssionDepth = 10;
 
dsMat2 = new BitmapMaterial (new ImageDS2 (1, 1), null, 5);
dsMat2.repeat = true; dsMat2.maxRecurssionDepth = 8;
 
dsMat3 = new BitmapMaterial (new ImageDS3 (1, 1), null, 5);
dsMat3.repeat = true; dsMat3.maxRecurssionDepth = 7;

So, you make 5 planes, arrange them to form canyon, assign materials, and add to scene:

// create planes
var pLeft:Plane3D = new Plane3D ("p_left", 10000, 10000, 1, 1, Plane3D.ZX_ALIGNED);
pLeft.appearance = new Appearance (dsMat1);
pLeft.enableClipping = true;
pLeft.x = -5000 -200; pLeft.y = -200; pLeft.z = +4000;
 
var pRiht:Plane3D = new Plane3D ("p_riht", 10000, 10000, 1, 1, Plane3D.ZX_ALIGNED);
pRiht.appearance = new Appearance (dsMat1);
pRiht.enableClipping = true;
pRiht.x = +5000 +200; pRiht.y = -200; pRiht.z = +4000;
 
var pSidL:Plane3D = new Plane3D ("p_sidL", 10000, 400, 1, 1, Plane3D.YZ_ALIGNED);
pSidL.appearance = new Appearance (dsMat2);
pSidL.enableClipping = true;
pSidL.x = -200; pSidL.y = -400; pSidL.z = +4000;
 
// we are actually going to be looking at plane backface
pSidL.enableBackFaceCulling = false;
 
var pSidR:Plane3D = new Plane3D ("p_sidR", 10000, 400, 1, 1, Plane3D.YZ_ALIGNED);
pSidR.appearance = new Appearance (dsMat2);
pSidR.enableClipping = true;
pSidR.x = +200; pSidR.y = -400; pSidR.z = +4000;
 
var pBott:Plane3D = new Plane3D ("p_bott", 10000, 400, 1, 1, Plane3D.ZX_ALIGNED);
pBott.appearance = new Appearance (dsMat3);
pBott.enableClipping = true;
pBott.y = -600; pBott.z = +4000;
 
// add them to scene
var scene:Group = new Group ("root");
 
scene.addChild (pLeft);
scene.addChild (pRiht);
scene.addChild (pSidL);
scene.addChild (pSidR);
scene.addChild (pBott);

Note that perspective correction will eat a lot of CPU unless you enable clipping on all your planes, since they are that large (hence .enableClipping = true;).

Now, if you would render the scene like that, here is how it would look like:

Terrible, isn't it? This is where tiling thing comes in. New setTiling method has two texture scaling parameters that allow you to tile your texture as many times as it takes to make large planes look acceptable in this case.

Additionally, setTiling allows you to specify texture offset. This is especially handy, since we can simulate continuous motion without actually moving geometry anywhere. For this purpose, we will set tiling in Event.ENTER_FRAME handler along with scene rendering:

dsMat1.setTiling (20, 20, 0, -2 * offset);
dsMat2.setTiling ( 1, 20, 0, -2 * offset);
dsMat3.setTiling ( 1, 10, 0, -1 * offset);

The thing that remains to do is to get offset changing and, perhaps, add a bit of interactivity:

// add some interactivity
offset += 2e-2 + 2e-4 * (300 - mouseY);
if (offset > 1) offset--;
world.camera.x = mouseX - 300;
world.camera.y = mouseY;
 
world.camera.lookAt (0, -200, 0);
world.render ();

And we're done. It's time to see the result!

The output

Move your mouse to control the scene.