Starry Trails - 3D Particle System ActionScript 3 source code

Another example of an AS3 3D Sprite based particle system. This one is using my 3D sprite engine but will port this to papervision asap. See it here.

The source code is by no means generic enough to be considered an engine or library, but is work in progress. I will extend the Node3D, SpringyNode3D classes to use Papervision3D and the Billboards for the rendering, and will definitely carry on working on the particle systems. I've also been converting a lot of noise functions to AS3 and will post them soon... its all getting quite exciting in the flash world! :P

This app is made of quite a few classes so Ill start from the top (application) downwards.

/***********************************************************************
MAIN DOCUMENT CLASS
make sure you have 2 sprites in your library exported for actionscript with linkage identifiers set to RedStar and BlueStar
 
***********************************************************************/	
 
package {
	import flash.display.*;
	import msa.sprite3d.*;
 
	public class StarryMouse3Dapp extends Sprite {
 
		public function StarryMouse3Dapp() {
			addChild( new ParticleSystem3D(2000, RedStar) );		// create a new ParticleSystem using the RedStar Class and add to display list
			addChild( new ParticleSystem3D(2000, BlueStar) );		// create a new ParticleSystem using the BlueStar Class and add to display list
 
			stage.align = StageAlign.TOP_LEFT;					// align stage to top left
			stage.scaleMode = StageScaleMode.NO_SCALE;			// and make flash not scale as it is resized but just extend to cover the new area
		}
 
	}
 
}

The following class is for the ParticleSystem3D. This just takes care of when to emit and update particles. It doesn't actually define individual particle behavior, but is like the container for all particles of a system.

/***********************************************************************
 
3D PARTICLE SYSTEM CLASS
Manages emitter, and all particles
v0.4.0
 
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
 
	import flash.geom.*;
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
 
	public class ParticleSystem3D extends Sprite {
		//emission properties: (should group these into an object, would allow flexibility in the future for more generic particle system)
		internal var gravity:Number			= 0.0;		// how much gravity there is 
		internal var fadeSpeed:Number		= 0.95;		// speed at which particles fade out
		internal var fadeStartTime:int		= 1000;		// milliseconds after which particles start fading out
		internal var growSpeed:Number		= 0.02;		// speed at which particles grow
		internal var emitSpeed:Number		= 5.0;		// max speed particles have when emitted
		internal var inheritSpeed:Number	= 0.6;		// how much of the emitters speed does the particle inherit
		internal var minSize:Number			= 0.02;		// minimum size of the particle
		internal var maxSize:Number			= 0.4;		// maximum size of the particle
		internal var maxRotSpeed:Number		= 10;		// maximum rotational speed
 
 
		internal var inheritVel:Vector3D;				// the actual velocity vector each particle will inherit (updated each frame by the particle system)
		internal var nowTime:int;						// milliseconds since app has been run
		internal var graphicClass:Class;
 
 
 
 
		private var _emitTimer:Timer;					// Timer instance to handle when to emit new particles
		private var _emitter:Emitter3D;					// handle to an emitter object
 
 
		public function ParticleSystem3D(freq:Number, graphicClass:Class) {
			this.graphicClass = graphicClass;
 
			_emitter = new Emitter3D(this, graphicClass);
			addChild(_emitter);
			_emitter.setPosition((Math.random()-0.5) * 1000, (Math.random()-0.5) * 1000, (Math.random()-0.5) * 1000);
 
			setFreq(freq);
 
			inheritVel = new Vector3D();
			addEventListener(Event.ENTER_FRAME, update);
		}
 
		public function setFreq(freq:Number) {
			_emitTimer = new Timer(1000/freq);
			_emitTimer.addEventListener(TimerEvent.TIMER, emit);
			startEmitter();
		}
 
		private function emit(e:TimerEvent) {
			addChild( new Particle3D(this, graphicClass, _emitter.pos.x, _emitter.pos.y, _emitter.pos.z) );
		}
 
		public function startEmitter() {
			_emitTimer.start();
		}
 
		public function stopEmitter() {
			_emitTimer.stop();
		}
 
 
		private function update(e:Event) {
			nowTime = getTimer();									// get the current time
 
			inheritVel = Vector3D.mult(_emitter.vel, inheritSpeed);	// calculate velocity inheritance vector
 
			for (var i:uint=0; i < numChildren; i++)				// loop through all children of the ParticleSystem (all particles and the _emitter object
				(getChildAt(i) as Node3D).updateAndRender();			// call its updateAndRender function
 
		}
 
	}
}

Next is the Particle3D class. This is the what each of the particles you see on screen are an instance of. Once a particle is emitted into the ParticleSystem3D, they live on their own. The ParticleSystem3D simply calls the update function for each particle (I did this to have more control as opposed to giving each particle its own enterframe handler). The constructor creates the particle at the emitters position with random velocity and other parameters, then the update function defines its behavior.

/***********************************************************************
 
GENERIC 3D PARTICLE CLASS
Takes care of an individual particle
v0.4.0
 
TODO:
create superclass for particles so different behaviours can be added quite easily
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
	import flash.utils.*	
 
	public class Particle3D extends Node3D {
		private var _age:int;
		private var _birthTime:int;
		private var _maxSize:Number;
 
		private var pSystem:ParticleSystem3D;
 
		public function Particle3D(pSystem:ParticleSystem3D, graphicClass:Class, tx:Number = 0, ty:Number = 0, tz:Number = 0) {
			super(tx, ty, tz);							// call super (Node3D) constructor
			this.pSystem = pSystem;						// save reference to the parent Particle System
 
			addChild( new graphicClass ); 		// add the relevant graphic from the library
 
			_birthTime = getTimer();
 
			size.x = size.y = 0.1;
			scaleX = scaleY = 0;
			_maxSize = pSystem.minSize + Math.random() * (pSystem.maxSize - pSystem.minSize);
 
			alpha = Math.random();
 
			vel = new Vector3D((Math.random()-0.5) * pSystem.emitSpeed, (Math.random()-0.5) * pSystem.emitSpeed, (Math.random()-0.5) * pSystem.emitSpeed);
			vel = Vector3D.add(vel, pSystem.inheritVel);
			vr = (Math.random() - 0.5) * pSystem.maxRotSpeed;// rotational velocity
			rotation = Math.random() * 360;
		}
 
 
		override public function update() {					// the particle behaviour is defined in here
			_age = pSystem.nowTime - _birthTime;
 
			vel.y += pSystem.gravity;
			if(size.x < _maxSize) size.x = size.y += pSystem.growSpeed;
			else size.x = size.y = _maxSize;
 
			if(_age > pSystem.fadeStartTime) {
				alpha *= pSystem.fadeSpeed;
				if(alpha<0.01) parent.removeChild(this);
			}
 
		}
 
	}
}

The next class is for the emitter. This is what the two big stars orbiting the mouse are instances of. The name of the class could be a bit misleading as it doesn't really take care of emitting particles (the ParticleSystem3D class does that), this class simply defines the physical behavior of the visual emitter object (in this case it behaves as if its orbiting the mouse cursor in 3D while attached to it with a spring - due to it extending the SpringyNode3D class)

/***********************************************************************
 
SPRINGY 3D PARTICLE EMITTER CLASS
v0.4.0
 
TODO:
create superclass for emitters so different behaviours can be added quite easily
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
 
	public class Emitter3D extends SpringyNode3D {
		private var _pSystem:ParticleSystem3D;
 
		private var _circleFreq:Number= 0.002;
		private var _circleRadius:Number= 200;
 
		public function Emitter3D(_pSystem:ParticleSystem3D, graphicClass:Class) {
			this._pSystem = _pSystem;
			addChild( new graphicClass ); 		// add the relevant graphic from the library
		}
 
 
		override public function update() {
			var sx:Number = Math.cos(_pSystem.nowTime * _circleFreq);
			var sy:Number = Math.sin(_pSystem.nowTime * _circleFreq);
			var rad:Number = Math.sin(_pSystem.nowTime * _circleFreq * 0.2);
 
			setTarget3(_pSystem.mouseX + (Math.random() - 0.5) * 200, _pSystem.mouseY + (Math.random() - 0.5) * 200,  0 + (Math.random() - 0.5) * 200);
			target.x += sx * _circleRadius * rad;
			target.z += sy * _circleRadius * rad;
 
			vel.z += (Math.random()-0.5) * (vel.x*vel.x + vel.y*vel.y) * 0.01;
 
			super.update(); // call the update function of the super class (SpringyNode3D) to do all spring functions
		}
	}
}

SpringyNode3D is the class which handles the springy behaviour of the 2 big stars.

/***********************************************************************
 
3D SPRINGY NODE CLASS
Creates a 3D sprite attached to a point in 3D space with spring of zero length and adjustable endpoint, stiffness, damping
v0.4.0
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
 
	public class SpringyNode3D extends Node3D {
		internal var target:Vector3D;
		internal var k:Number		= 0.05;			// default values
		internal var d:Number		= 0.1;
 
		public function SpringyNode3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) {
			super(tx, ty, tz);
			target = new Vector3D();
		}
 
		public function setSpring(k, d) {
			this.k = k;
			this.d = d;
		}
 
		public function setTarget(t:Vector3D) {
			target = t.clone();
		}
 
		public function setTarget3(x, y, z) {
			target.set(x, y, z);
		}		
 
 
		override public function update() {					// add spring forces to the velocity
			vel.x += (target.x - pos.x) * k - vel.x * d;
			vel.y += (target.y - pos.y) * k - vel.y * d;
			vel.z += (target.z - pos.z) * k - vel.z * d;
		}
 
	}
}

The next 2 classes I will scrap in favor of Papervision3Ds billboard rendering when I get a chance. First one is Node3D which simply is a physical node, in 3D space.

/***********************************************************************
 
GENERIC 3D SPRITE NODE CLASS
Renders as a 3D sprite and has basic physical properties like velocity, momentum etc.
v0.4.0
 
TODO:
add camera functionilty instead of fixed projection
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
 
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.geom.ColorTransform;
 
	public class Node3D extends Sprite {
		public var pos:Vector3D;			// contains x,y,z position
		public var vel:Vector3D;			// contains velocity vector
		public var size:Point;				// the x and y size of the object
		public var oldPos:Vector3D;			// the old position
		public var drag:Number;				// a kind of fake/simplified friction
		public var vr:Number;				// rotational velocity		
 
		static var MAX_Z:Number = 400;		// distance of camera to the front plane, decreasing this increases the FOV. Increasing it decreases the FOV.
 
		private var invZ:Number;			// used for internal calculations
 
		public function Node3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) {
			size = new Point(1, 1);
			pos = new Vector3D(tx, ty, tz);
			vel = new Vector3D(0, 0, 0);
		}
 
		public function setPosition(tx:Number = 0, ty:Number = 0, tz:Number = 0) {
			pos.set(tx, ty, tz);
		}
 
		public function updateAndRender() {		// transforms the 3D position/scale etc. to 2D stage coordinates 
			update();
 
 
			oldPos = pos.clone();				// copy position to old position
 
			pos = Vector3D.add(pos, vel);		// add velocity to position
 
			if(stage && pos.z > -MAX_Z) {				// if the sprite is beyong the front clipping plane and should be rendered;
 
				invZ = MAX_Z/(MAX_Z + pos.z);		// calculate transformation amount
 
				var ct:ColorTransform = transform.colorTransform;	
				ct.redMultiplier = ct.greenMultiplier = ct.blueMultiplier = invZ;		// applies black fog, can be changed to any ColorTransform
				transform.colorTransform = ct;			
 
				var cx = stage.stageWidth/2;
				var cy = stage.stageHeight/2;
				x = (pos.x - cx) * invZ + cx;				// transform 3D coordinates to 2D
				y = (pos.y - cy) * invZ + cy;
 
				scaleX = size.x * invZ;						// calculate size based on distance
				scaleY = size.y * invZ;
				rotation += vr;				
			}
				// else visible = false					// front clipping (I want objects in front of the front clipping plane to 'stick' to the camera so commented out this line
		}
 
 
		public function update() {}						// this will be overridden, should be a pure virtual function if flash supported it!
 
	}
}

and the simple 3D Vector with a few 3D arithmetic (analogous to Papervisions Number3D class).

/***********************************************************************
 
BASIC 3D VECTOR CLASS
v0.8.0
 
 
-----------------------------------
 
Copyright (c) 2008, Memo Akten, www.memo.tv
 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
***********************************************************************/	
 
package msa.sprite3d {
 
	public class Vector3D {
		public var x:Number;
		public var y:Number;
		public var z:Number;
 
		// constructor
		public function Vector3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) 	{	set(tx, ty, tz);		}
 
		// set the x,y,z values in one go
		public function set(tx:Number = 0, ty:Number = 0, tz:Number = 0)		{	x = tx;	y = ty;	z = tz;		}
 
		// subtract vector p2 from p1
		public static function sub(p1:Vector3D, p2:Vector3D):Vector3D			{	return (new Vector3D(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z) ); 		}
 
		// add vectors p1 and p2
		public static function add(p1:Vector3D, p2:Vector3D):Vector3D			{	return (new Vector3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z) );		}
 
		// multiply vector p with number n
		public static function mult(p:Vector3D, n:Number):Vector3D				{	return (new Vector3D(p.x * n, p.y * n, p.z * n) );		}
 
		// returns a copy (new instance) of the vector
		public function clone():Vector3D 										{	return (new Vector3D(x, y, z) );		}
 
	}
}

stars.jpg
AttachmentSize
download source files here23.67 KB
view finished SWF3.02 KB