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 Flash Engineer PopCap Games, International Ltd.

Portfolio

UV Scrolling in Starling


Obviously this could come in pretty darned handy for space games, side scrollers, etc, etc[...]

Drawing on Stuff in Away3D 4.0

So, Easter Day, I thought I’d sit down and make a little ‘Paint on an Egg and Send it to Your Friend’ app.

Santabot: A Unity3D Flash Game


All right, so a Christmas game like “Santabot vs. The Flying Saucers from Mars” may be a day late[...]

Magnify – a jQuery Plugin


Let me begin by saying right up front, I have not given up on Flash[...]

It’s a Starling Halloween


Getting some practice for the upcoming Zombie Apocalypse[...]

Getting Started with Proscenium

So I had a chance this weekend to sit down and play around with Adobe’s new 3D framework for Stage3D, Proscenium, and thought I’d share a few of the results (a word of caution, there are no preloaders for any of the examples and may load a bit slowly). The first shows some reflections and [...]

Particle Editor for Starling Framework

An in-browser particle editor for the Starling 2D Framework for Flash Player 11.

So Long and Thanks for all the Flash on the Beach

So, another Flash on the Beach has just has just drawn to a close[...]

Game Development Tips from the Trenches of PopCap

Well, this is a post that’s a bit overdue, but, thanks to a well timed bank holiday, I finally had the opportunity to sit down and type up what I’ve been meaning to for some time now…

Old Skool Demoscene FX as 3D Textures

Many moons ago, I got the idea to create some demoscene plane deformation effects in Flash based on the formulas found here: http://www.iquilezles.org/www/articles/deform/deform.htm. I posted my less than desired results up on wonderfl. Thankfully, fellow wonderfl user, Hasufel, forked my attempt and optimized the hell out of it coming up with this. Well, today, for no [...]

Making The Gaming Scene (A Change in Careers)

And for my second blog post of the day, a much more personal note. After about three years of working with, what I would consider as objectively as possible, the best digital agency in Ireland, vStream Digital Media, I have made the immense career decision to leave the agency world and enter the arena of [...]

Feeling Lucky?

Images to dice, kick ascii style [...]

Adventures in Playbook Land

Adventures in Playbook Land


Now that the ordeal is over, I thought I’d take the time to sit down and share my account of what it was like to develop a Blackberry Playbook application using the Adobe Flex SDK[...]

Flash

Draw it for Me

So many ideas – so little time….

Kinect Application Running in Dublin

So, on Friday I just wrapped up our latest project at vStream Digital Media, a Kinect powered flipbook that lets users flip through the hand written notebooks of Philip Lynott of Thin Lizzy. The app uses OpenNI, runs in Adobe AIR and is currently on display at a pretty bitchin’ Phil Lynott exhibition running in [...]

Beach Ball Kinect Party

So we finally got a Kinect camera hooked up to a pc at work and, while it doesn’t seem to be legal to use it for commercial projects (but, hey, I’m no lawyer), the boss asked me to get it figured out and come up with some ideas just in case it would be feasible [...]