Rockin and Rollin with the JiglibFlash Terrain

Finally got the chance to play around with the new JiglibFlash height map terrain, today, and have to say I am very impressed!

The first thing I noticed missing, though, was the ability to update the terrain in order to use animated height maps. I tried deleting then creating a new terrain inside a rendering event handler. This actually worked better than it sounds like it might. At least for a very short time. Going this route, within less than a minute, the frame rate dropped from around 58 to around 15 and the memory usage was well over 400 and climbing steadily to an inevitable crash. I knew an update function was needed for any animation.

Before I dig into the nerd stuff, have a look at what I’m talking about. Double click the example below to see an animated JTerrain instance in action (double click on it a second time to stop the animation and save a bit of processor power). And this is just using perlin noise. You could also use sound data (mp3 or microphone), video data, etc. etc (giving myself some ideas and goosebumps just thinking about it).

Get Adobe Flash player

If you’d like to try this out yourself, here’s the changes I made to a few of the jiglib core classes (for Papervision3D only, so far). In ITerrain.as add just a simple function update():void to the interface methods.

In JTerrain, you’ll need to add a quick update method

public function update():void {
	_terrain.update();
}

The tricky part comes in pv3dTerrain.as. First you’ll need to add some private variables to keep a few things persistent:

private var _map:BitmapData;
private var _d:Number;
private var _w:Number;
private var _maxHH:Number;

In the constructor, you’ll actually want to set those vars:

// set for updating
_map = terrainHeightMap;
_d = depth;
_w = width;
_maxHH = maxHeight;

In the build terrain method, you’ll want to change these 2 lines:

var vertices :Array  = this.geometry.vertices;
var faces    :Array  = this.geometry.faces	;

to:

var vertices :Array  = this.geometry.vertices	= [];
var faces    :Array  = this.geometry.faces		= [];

in order to create the vertices and faces arrays from scratch every time this method is called.

Finally, still in the pv3dTerrain.as file, you’ll need to add an update method that calls the buildTerrain method using our stored variables like so:

public function update():void {
	buildTerrain(_w, _d, _maxHH, _map);
}

Now, keep in mind that doing this may add to a bit more memory consumption when not using animated terrains, as a reference to your bitmapdata is now stored within your terrain instance (but you always destroy bitmapdata when through with it, right?), plus, who knows, it may just break something down the road, so I’d recommend keeping a back up of the original files if you give this a try.

In any case, when done, you can create the example above with the script below:

package {
 
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
 
	import jiglib.geometry.JSphere;
	import jiglib.geometry.JTerrain;
	import jiglib.plugin.papervision3d.Papervision3DPhysics;
	import jiglib.plugin.papervision3d.Pv3dMesh;
 
	import net.hires.debug.Stats;
 
	import org.papervision3d.cameras.Camera3D;
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.materials.shadematerials.PhongMaterial;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Sphere;
	import org.papervision3d.render.BasicRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.view.Viewport3D;
 
	/**
	 * Animated JiglibFlash terrain example
	 * @author Devon O.
	 */
 
	[SWF(width='550', height='400', backgroundColor='#000000', frameRate='60')]
	public class Main extends Sprite {
 
		public static const NUM_BALLS:int = 5;
 
		private var _scene:Scene3D;
		private var _cam:Camera3D;
		private var _light:PointLight3D;
		private var _view:Viewport3D;
		private var _eng:BasicRenderEngine;
 
		private var _lightAngle:Number = 0;
 
		private var _terrain:JTerrain;
		private var _terrainMap:BitmapData;
		private var _offsetPoint:Point;
		private var _seed:int;
 
		private var _physics:Papervision3DPhysics;
 
		private var _balls:Vector. = new Vector.(NUM_BALLS, true);
 
		private var _appSeen:Boolean = false;
		private var _noApp:Sprite;
 
		public function Main():void {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
 
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
 
			init3D();
			initPhysics();
			initScene();
 
			addChild(new Stats());
 
			_noApp = createNoApp();
			addChild(_noApp);
 
			stage.doubleClickEnabled = true;
			stage.addEventListener(MouseEvent.DOUBLE_CLICK, startStop);
		}
 
		private function startStop(event:MouseEvent):void {
			if (_appSeen) {
				stopRendering();
				addChild(_noApp);
			} else {
				startRendering();
				if (contains(_noApp)) removeChild(_noApp);
			}
			_appSeen = !_appSeen;
		}
 
		private function createNoApp():Sprite {
			var s:Sprite = new Sprite();
			s.mouseEnabled = false;
			s.graphics.beginFill(0x000000);
			s.graphics.drawRect(0, 0, 550, 400);
			s.graphics.endFill();
			var tf:TextField = new TextField();
			tf.defaultTextFormat = new TextFormat("_sans", 12, 0xFFFFFF);
			tf.selectable = false;
			tf.mouseEnabled = false;
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.text = "Double Click to Start.";
			tf.x = Math.round(550 * .5 - tf.width * .5);
			tf.y = Math.round(400 * .5 - tf.height * .5);
			s.addChild(tf);
			return s;
		}
 
		private function init3D():void {
			_view = new Viewport3D(stage.stageWidth, stage.stageHeight);
			addChild(_view);
 
			_cam = new Camera3D();
			_cam.z = -500;
			_cam.y = 400;
 
			_light = new PointLight3D();
			_light.y = 500;
 
			_cam.target = new DisplayObject3D();
 
			_scene = new Scene3D();
			_eng = new BasicRenderEngine();
		}
 
		private function initPhysics():void {
			_physics = new Papervision3DPhysics(_scene, 8);
		}
 
		private function initScene():void {
			// terrain stuff
			var tmat:PhongMaterial = new PhongMaterial(_light, 0x000077, 0x000000, 0);
			_terrainMap = new BitmapData(100, 100, false);
			_seed = int(Math.random() * 10000);
			_offsetPoint = new Point();
 
			_terrain = _physics.createTerrain(_terrainMap, tmat, 600, 600, 200, 9, 9);
 
			// just so you can see the bitmapdata creating the terrain
			var bmp:Bitmap = new Bitmap(_terrainMap);
			bmp.y = 300;
			addChild(bmp);
 
			// add the balls to the mix
			var bmat:PhongMaterial = new PhongMaterial(_light, 0xFF00FF, 0x000000, 0);
			for (var i:int = 0; i < NUM_BALLS; i++) {
				var ball:Sphere = new Sphere(bmat, 30);
				_scene.addChild(ball);
 
				// keep balls in front of terrain at all times with line below
				//_view.getChildLayer(ball).layerIndex = (i+1);
 
				var jball:JSphere = new JSphere(new Pv3dMesh(ball), 30);
				jball.moveTo(new Vector3D(0, i * 200, 0));
				_balls[i] = jball;
				_physics.addBody(jball);
			}
		}
 
		private function startRendering():void {
			addEventListener(Event.ENTER_FRAME, render);
		}
 
		private function stopRendering():void {
			removeEventListener(Event.ENTER_FRAME, render);
		}
 
		private function render(event:Event):void {
			// wrinkle up then update the terrain
			_offsetPoint.x += 1;
			_offsetPoint.y -= 1;
			_terrainMap.perlinNoise(50, 50, 1, _seed, true, false, 7, true, [_offsetPoint]);
 
			_terrain.update();
 
			// orbit the light around
			_light.z = Math.sin(_lightAngle) * 200;
			_light.x = Math.cos(_lightAngle) * 200;
			_lightAngle += 0.05;
 
			// ease the camera back and forth according to mouse pos
			var ratio:Number = ((stage.mouseX / stage.stageWidth) - .5) * 2;
			_cam.x += ((ratio * 500) - _cam.x) * .1;
 
			// check on the family jewels
			var i:int = NUM_BALLS;
			while (i--) {
				var jball:JSphere = _balls[i];
				if ((jball.z < -300) || (jball.z > 300) || (jball.x < -300) || (jball.x > 300)) jball.moveTo(new Vector3D(0, 400, 0));
			}
 
			// render it all
			_physics.engine.integrate(0.2);
			_eng.renderScene(_scene, _cam, _view);
		}
	}
}

Next up – a little ragdoll action… Woot!

8 Comments »

  1. longerlife says:

    Nice work… (as always).

  2. SteenMSU says:

    Thank you so much, I’ve been looking for a way to update the terrain for a few weeks now. You the man!

  3. Devon O. says:

    No – YOU the man.. :)
    You really from MSU? Went to UofM, myself.. many many moons ago, that is…
    Have fun with the terrain, but a word of warning, seems more than 8 or so horizontal/vertical segments and it no longer seems to work so well (see the post after this)..

  4. SteenMSU says:

    Ha ha, what a small world. I just finished my Masters at MSU in December. Thanks for the heads up :)

  5. Patrick says:

    Very helpful! I was working on something similar where I needed regular updates to the JTerrain and MAN, the memory was going through the roof! Your solution was exactly what I needed.

  6. Patrick says:

    Thanks again Devon! Here’s my terrain painting experiment that greatly benefited from your post: http://blog.ironandroid.com/?p=23. Wouldn’t mind a comment if you had the time. Download the source and have fun.

  7. [...] doesn’t release its mesh nor allow you a clean way to make updates. Luckily I found this post over on Devon Wolfgang’s onebyoneblog which had a solution. To get this to work I had to make [...]

RSS feed for comments on this post. / TrackBack URI

Leave a Reply

Devon O. Wolfgang

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development”

Contributing Author of “Flash AS3 for Interactive Agencies”

Senior Software Developer in Dublin, Ireland

Portfolio

Bayer Pixel Bender Filter

Bayer Mosaic Filter in Pixel Bender


Now, seeing as how I’ve been trying to learn some Pixel Bender coding lately[...]

Liquitext

Liquid Text Effect


Haven’t been too active around here lately due to a major on going project at work lately as well as the fact that my wife and I are in the slow process of moving homes. Really, the only free time it seems I have these days is during the lunch hour. So, over the past two days, I came up with this fun little toy during lunches[...]

Google’s Text To Speech Engine in Flash

I’m not sure how I managed to miss this, but I just happened to run across a ‘new’ (well, newish), albeit still unofficial, offering of Google today: text-to-speech. You can see what few details there are on this Techcrunch post. Basically, it just boils down to this though – you send your phrase to be [...]

Quick Sound Effects Generator


Need some beeps, boops, or bops, to go with your UI or games [...]

Quick QuickBox2D Tip II – Collision Detection

Custom collision detection/handling in QuickBox2D [...]

None of This Runs Eternal


No Flash/Actionscript stuff here. Just me rambling about the upcoming Current 93 concert [...]

Playing Around with the New UndoManager

Included in the Flex 4.0 SDK and the, just released, Flash Professional CS5 lies a new hidden little gem of a class: flashx.undo.UndoManager (although the Flex 4.0 SDK’s been out for awhile, I have to admit I didn’t even notice this until I installed Flash CS5 and started poking around the documentation looking for new [...]

Making Waves

In a previous post, “Digging into the Microphone in Flash Player 10.1″, reader David Law asked in the comments how it would be possible to save .wav files to the server. I wasn’t sure right offhand, but thought I’d spend my lunch hour yesterday looking into it. Well, after reading this quick tutorial on Adobe [...]

Some Drawing Fun and a Bad Movie You’ll Never See

Earlier today, Dave Gillem posted a link on Facebook to an incredible Processing based drawing application. Thought I’d have a go at reproducing something similar in Flash. Well, I failed miserably, but the results were still interesting enough to check out. You can play around with it below. Just mouse down to draw in the [...]

Animating Bezier Curves


The other day I got the notion in my head that I wanted to draw some bezier curves in an animated fashion. I’m sure this has been done a million times before, but sometimes reinventing the wheel can be a good learning experience [...]

My God, It’s Full of Stars…


Just a quick little thing I got an idea for (i.e. pretty much ripped off) while browsing Processing examples.

Another Year, Another Look

As a few folks may have noticed, the blog is looking a little bit different as of today [...]

The Webcam Warholizer


Just a little bit of quick Friday fun with webcam and bitmapata…

More With the JiglibFlash Terrain

Playing around a bit more with the animated JiglibFlash terrain idea from my last post, I wanted to see how it would work in conjunction with a webcam. So, needless to say, you’ll need a webcam to check out the results below.

Rockin and Rollin with the JiglibFlash Terrain


Finally got the chance to play around with the new JiglibFlash height map terrain, today, and have to say I am very impressed [...]