fire effect for starling
starling particle effect
ref: http://wiki.starling-framework.org/extensions/particlesystem
/**
* Copyright flashisobar ( http://wonderfl.net/user/flashisobar )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/wmXO
*/
// forked from ProjectNya's Starling [color]
////////////////////////////////////////////////////////////////////////////////
// Starling [color]
//
// [AS3.0] Starlingを試すのだ! (4)
// http://www.project-nya.jp/modules/weblog/details.php?blog_id=1622
////////////////////////////////////////////////////////////////////////////////
package
{
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.events.Event;
import flash.system.Capabilities;
import flash.display.Bitmap;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.display.Loader;
import starling.core.Starling;
[SWF(backgroundColor="#000000",width="465",height="465",frameRate="60")]
public class Main extends flash.display.Sprite
{
private const IMAGE_URL:String = "http://assets.wonderfl.net/images/related_images/3/37/3794/3794bfc95c5b2fe09075203e67cd1e40ff0e4a14m";
///private var source:BitmapData = new BitmapData(465, 465, false, 0x000000);
private var loader:Loader;
private var starling:Starling
public static var bmp:Bitmap;
public static var psConfig:XML = <particleEmitterConfig>
<texture name="p.png"/>
<sourcePosition x="160.55" y="428.95"/>
<sourcePositionVariance x="370.00" y="0.00"/>
<speed value="90.00"/>
<speedVariance value="30.00"/>
<particleLifeSpan value="1.0000"/>
<particleLifespanVariance value="0.9000"/>
<angle value="270.37"/>
<angleVariance value="15.00"/>
<gravity x="0.00" y="0.00"/>
<radialAcceleration value="0.00"/>
<tangentialAcceleration value="0.00"/>
<radialAccelVariance value="0.00"/>
<tangentialAccelVariance value="0.00"/>
<startColor red="1.00" green="0.31" blue="0.00" alpha="0.62"/>
<startColorVariance red="0.00" green="0.00" blue="0.00" alpha="0.00"/>
<finishColor red="1.00" green="0.31" blue="0.00" alpha="0.00"/>
<finishColorVariance red="0.00" green="0.00" blue="0.00" alpha="0.00"/>
<maxParticles value="500"/>
<startParticleSize value="70.00"/>
<startParticleSizeVariance value="49.53"/>
<finishParticleSize value="10.00"/>
<FinishParticleSizeVariance value="0.00"/>
<duration value="-1.00"/>
<emitterType value="0"/>
<maxRadius value="100.00"/>
<maxRadiusVariance value="0.00"/>
<minRadius value="0.00"/>
<rotatePerSecond value="0.00"/>
<rotatePerSecondVariance value="0.00"/>
<blendFuncSource value="770"/>
<blendFuncDestination value="1"/>
<rotationStart value="0.00"/>
<rotationStartVariance value="0.00"/>
<rotationEnd value="0.00"/>
<rotationEndVariance value="0.00"/>
</particleEmitterConfig>
;
//private var source:BitmapData = new BitmapData(465, 465, false, 0x000000);
public function Main()
{
Wonderfl.disable_capture();
///addChild(new Bitmap(source));
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
init();
}
private function init():void
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(IMAGE_URL), new LoaderContext(true));
}
private function onComplete(e:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
bmp = e.target.content as Bitmap;
start();
}
private function start():void
{
starling = new Starling(MainView, stage, null, null);
starling.enableErrorChecking = Capabilities.isDebugger;
starling.showStats = true;
starling.start();
//addEventListener(Event.ENTER_FRAME, ready, false, 0, true);
}
/*
private function ready(evt:Event):void {
removeEventListener(Event.ENTER_FRAME, ready);
starling.shareContext = true;
addEventListener(Event.ENTER_FRAME, update, false, 0, true);
}
private function update(evt:Event):void {
starling.context.clear();
starling.render();
starling.context.drawToBitmapData(source);
starling.context.present();
}
*/
}
}
//////////////////////////////////////////////////
// internal class MainView
//////////////////////////////////////////////////
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import starling.core.Starling;
import starling.display.Sprite;
import starling.events.Event;
import starling.textures.Texture;
import starling.display.Image;
class MainView extends Sprite
{
private var ps:PDParticleSystem
public function MainView()
{
addEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.REMOVED_FROM_STAGE, remove);
}
private function init(evt:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
start();
}
private function start():void
{
// bg
var bmd:BitmapData = new BitmapData(465, 465, false, 0x0);
bmd.perlinNoise(100, 100, 6, Math.floor(Math.random() * 10), false, true, BitmapDataChannel.RED);
var bmp:Bitmap = new Bitmap(bmd);
var texture:Texture = Texture.fromBitmap(bmp, false);
var image:Image = new Image(texture);
addChild(image);
// particle texture
var p_texture:Texture = Texture.fromBitmap(Main.bmp, false);
// create particle system
ps = new PDParticleSystem(Main.psConfig, p_texture);
addChild(ps);
// add it to a juggler (or call its advanceTime method once per frame) to animate it.
Starling.juggler.add(ps);
ps.start();
// set position
ps.x = 100;
ps.y = 465;
}
private function remove(evt:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, remove);
Starling.juggler.remove(ps);
}
override public function dispose():void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
removeEventListener(Event.REMOVED_FROM_STAGE, remove);
super.dispose();
}
}
// =================================================================================================
//
// Starling Framework - Particle System Extension
// Copyright 2011 Gamua OG. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
class ColorArgb
{
public var red:Number;
public var green:Number;
public var blue:Number;
public var alpha:Number;
public function ColorArgb(red:Number = 0, green:Number = 0, blue:Number = 0, alpha:Number = 0)
{
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
}
public function toRgb():uint
{
var r:Number = red;
if (r < 0.0)
r = 0.0;
else if (r > 1.0)
r = 1.0;
var g:Number = green;
if (g < 0.0)
g = 0.0;
else if (g > 1.0)
g = 1.0;
var b:Number = blue;
if (b < 0.0)
b = 0.0;
else if (b > 1.0)
b = 1.0;
return int(r * 255) << 16 | int(g * 255) << 8 | int(b * 255);
}
public function toArgb():uint
{
var a:Number = alpha;
if (a < 0.0)
a = 0.0;
else if (a > 1.0)
a = 1.0;
var r:Number = red;
if (r < 0.0)
r = 0.0;
else if (r > 1.0)
r = 1.0;
var g:Number = green;
if (g < 0.0)
g = 0.0;
else if (g > 1.0)
g = 1.0;
var b:Number = blue;
if (b < 0.0)
b = 0.0;
else if (b > 1.0)
b = 1.0;
return int(a * 255) << 24 | int(r * 255) << 16 | int(g * 255) << 8 | int(b * 255);
}
}
class Particle
{
public var x:Number;
public var y:Number;
public var scale:Number;
public var rotation:Number;
public var color:uint;
public var alpha:Number;
public var currentTime:Number;
public var totalTime:Number;
public function Particle()
{
x = y = rotation = currentTime = 0.0;
totalTime = alpha = scale = 1.0;
color = 0xffffff;
}
}
class PDParticle extends Particle
{
public var colorArgb:ColorArgb;
public var colorArgbDelta:ColorArgb;
public var startX:Number, startY:Number;
public var velocityX:Number, velocityY:Number;
public var radialAcceleration:Number;
public var tangentialAcceleration:Number;
public var emitRadius:Number, emitRadiusDelta:Number;
public var emitRotation:Number, emitRotationDelta:Number;
public var rotationDelta:Number;
public var scaleDelta:Number;
public function PDParticle()
{
colorArgb = new ColorArgb();
colorArgbDelta = new ColorArgb();
}
}
import com.adobe.utils.AGALMiniAssembler;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.VertexBuffer3D;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import starling.animation.IAnimatable;
import starling.core.RenderSupport;
import starling.core.Starling;
import starling.display.DisplayObject;
import starling.errors.MissingContextError;
import starling.events.Event;
import starling.textures.Texture;
import starling.utils.MatrixUtil;
import starling.utils.VertexData;
class ParticleSystem extends DisplayObject implements IAnimatable
{
private static const PROGRAM_MIPMAP:String = "PS_mm";
private static const PROGRAM_NO_MIPMAP:String = "PS_nm";
private var mTexture:Texture;
private var mParticles:Vector.<Particle>;
private var mFrameTime:Number;
private var mVertexData:VertexData;
private var mVertexBuffer:VertexBuffer3D;
private var mIndices:Vector.<uint>;
private var mIndexBuffer:IndexBuffer3D;
private var mNumParticles:int;
private var mMaxCapacity:int;
private var mEmissionRate:Number; // emitted particles per second
private var mEmissionTime:Number;
/** Helper objects. */
private static var sHelperMatrix:Matrix = new Matrix();
private static var sHelperPoint:Point = new Point();
private static var sRenderAlpha:Vector.<Number> = new <Number>[1.0, 1.0, 1.0, 1.0];
protected var mEmitterX:Number;
protected var mEmitterY:Number;
protected var mPremultipliedAlpha:Boolean;
protected var mBlendFactorSource:String;
protected var mBlendFactorDestination:String;
public function ParticleSystem(texture:Texture, emissionRate:Number, initialCapacity:int = 128, maxCapacity:int = 8192, blendFactorSource:String = null, blendFactorDest:String = null)
{
if (texture == null)
throw new ArgumentError("texture must not be null");
mTexture = texture;
mPremultipliedAlpha = texture.premultipliedAlpha;
mParticles = new Vector.<Particle>(0, false);
mVertexData = new VertexData(0);
mIndices = new <uint>[];
mEmissionRate = emissionRate;
mEmissionTime = 0.0;
mFrameTime = 0.0;
mEmitterX = mEmitterY = 0;
mMaxCapacity = Math.min(8192, maxCapacity);
mBlendFactorDestination = blendFactorDest || Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
mBlendFactorSource = blendFactorSource || (mPremultipliedAlpha ? Context3DBlendFactor.ONE : Context3DBlendFactor.SOURCE_ALPHA);
registerPrograms();
raiseCapacity(initialCapacity);
// handle a lost device context
Starling.current.addEventListener(Event.CONTEXT3D_CREATE, onContextCreated);
}
public override function dispose():void
{
if (mVertexBuffer)
mVertexBuffer.dispose();
if (mIndexBuffer)
mIndexBuffer.dispose();
Starling.current.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreated);
super.dispose();
}
private function onContextCreated(event:Event):void
{
registerPrograms();
raiseCapacity(0);
}
protected function createParticle():Particle
{
return new Particle();
}
protected function initParticle(particle:Particle):void
{
particle.x = mEmitterX;
particle.y = mEmitterY;
particle.currentTime = 0;
particle.totalTime = 1;
particle.color = Math.random() * 0xffffff;
}
protected function advanceParticle(particle:Particle, passedTime:Number):void
{
particle.y += passedTime * 250;
particle.alpha = 1.0 - particle.currentTime / particle.totalTime;
particle.scale = 1.0 - particle.alpha;
particle.currentTime += passedTime;
}
private function raiseCapacity(byAmount:int):void
{
var oldCapacity:int = capacity;
var newCapacity:int = Math.min(mMaxCapacity, capacity + byAmount);
var context:Context3D = Starling.context;
if (context == null)
throw new MissingContextError();
var baseVertexData:VertexData = new VertexData(4);
baseVertexData.setTexCoords(0, 0.0, 0.0);
baseVertexData.setTexCoords(1, 1.0, 0.0);
baseVertexData.setTexCoords(2, 0.0, 1.0);
baseVertexData.setTexCoords(3, 1.0, 1.0);
mTexture.adjustVertexData(baseVertexData, 0, 4);
mParticles.fixed = false;
mIndices.fixed = false;
for (var i:int = oldCapacity; i < newCapacity; ++i)
{
var numVertices:int = i * 4;
mParticles.push(createParticle());
mVertexData.append(baseVertexData);
mIndices.push(numVertices, numVertices + 1, numVertices + 2, numVertices + 1, numVertices + 3, numVertices + 2);
}
mParticles.fixed = true;
mIndices.fixed = true;
// upload data to vertex and index buffers
if (mVertexBuffer)
mVertexBuffer.dispose();
if (mIndexBuffer)
mIndexBuffer.dispose();
mVertexBuffer = context.createVertexBuffer(newCapacity * 4, VertexData.ELEMENTS_PER_VERTEX);
mVertexBuffer.uploadFromVector(mVertexData.rawData, 0, newCapacity * 4);
mIndexBuffer = context.createIndexBuffer(newCapacity * 6);
mIndexBuffer.uploadFromVector(mIndices, 0, newCapacity * 6);
}
public function start(duration:Number = Number.MAX_VALUE):void
{
if (mEmissionRate != 0)
mEmissionTime = duration;
}
public function stop(clear:Boolean = false):void
{
mEmissionTime = 0.0;
if (clear)
mNumParticles = 0;
}
/** Returns an empty rectangle at the particle system's position. Calculating the
* actual bounds would be too expensive. */
public override function getBounds(targetSpace:DisplayObject, resultRect:Rectangle = null):Rectangle
{
if (resultRect == null)
resultRect = new Rectangle();
getTransformationMatrix(targetSpace, sHelperMatrix);
MatrixUtil.transformCoords(sHelperMatrix, 0, 0, sHelperPoint);
resultRect.x = sHelperPoint.x;
resultRect.y = sHelperPoint.y;
resultRect.width = resultRect.height = 0;
return resultRect;
}
public function advanceTime(passedTime:Number):void
{
var particleIndex:int = 0;
var particle:Particle;
// advance existing particles
while (particleIndex < mNumParticles)
{
particle = mParticles[particleIndex] as Particle;
if (particle.currentTime < particle.totalTime)
{
advanceParticle(particle, passedTime);
++particleIndex;
}
else
{
if (particleIndex != mNumParticles - 1)
{
var nextParticle:Particle = mParticles[int(mNumParticles - 1)] as Particle;
mParticles[int(mNumParticles - 1)] = particle;
mParticles[particleIndex] = nextParticle;
}
--mNumParticles;
if (mNumParticles == 0)
dispatchEvent(new Event(Event.COMPLETE));
}
}
// create and advance new particles
if (mEmissionTime > 0)
{
var timeBetweenParticles:Number = 1.0 / mEmissionRate;
mFrameTime += passedTime;
while (mFrameTime > 0)
{
if (mNumParticles < mMaxCapacity)
{
if (mNumParticles == capacity)
raiseCapacity(capacity);
particle = mParticles[int(mNumParticles++)] as Particle;
initParticle(particle);
advanceParticle(particle, mFrameTime);
}
mFrameTime -= timeBetweenParticles;
}
if (mEmissionTime != Number.MAX_VALUE)
mEmissionTime = Math.max(0.0, mEmissionTime - passedTime);
}
// update vertex data
var vertexID:int = 0;
var color:uint;
var alpha:Number;
var rotation:Number;
var x:Number, y:Number;
var xOffset:Number, yOffset:Number;
var textureWidth:Number = mTexture.width;
var textureHeight:Number = mTexture.height;
for (var i:int = 0; i < mNumParticles; ++i)
{
vertexID = i << 2;
particle = mParticles[i] as Particle;
color = particle.color;
alpha = particle.alpha;
rotation = particle.rotation;
x = particle.x;
y = particle.y;
xOffset = textureWidth * particle.scale >> 1;
yOffset = textureHeight * particle.scale >> 1;
for (var j:int = 0; j < 4; ++j)
{
mVertexData.setColor(vertexID + j, color);
mVertexData.setAlpha(vertexID + j, alpha);
}
if (rotation)
{
var cos:Number = Math.cos(rotation);
var sin:Number = Math.sin(rotation);
var cosX:Number = cos * xOffset;
var cosY:Number = cos * yOffset;
var sinX:Number = sin * xOffset;
var sinY:Number = sin * yOffset;
mVertexData.setPosition(vertexID, x - cosX + sinY, y - sinX - cosY);
mVertexData.setPosition(vertexID + 1, x + cosX + sinY, y + sinX - cosY);
mVertexData.setPosition(vertexID + 2, x - cosX - sinY, y - sinX + cosY);
mVertexData.setPosition(vertexID + 3, x + cosX - sinY, y + sinX + cosY);
}
else
{
// optimization for rotation == 0
mVertexData.setPosition(vertexID, x - xOffset, y - yOffset);
mVertexData.setPosition(vertexID + 1, x + xOffset, y - yOffset);
mVertexData.setPosition(vertexID + 2, x - xOffset, y + yOffset);
mVertexData.setPosition(vertexID + 3, x + xOffset, y + yOffset);
}
}
}
public override function render(support:RenderSupport, alpha:Number):void
{
if (mNumParticles == 0)
return;
// always call this method when you write custom rendering code!
// it causes all previously batched quads/images to render.
support.finishQuadBatch();
// make this call to keep the statistics display in sync.
// to play it safe, it's done in a backwards-compatible way here.
if (support.hasOwnProperty("raiseDrawCount"))
support.raiseDrawCount();
alpha *= this.alpha;
var program:String = mTexture.mipMapping ? PROGRAM_MIPMAP : PROGRAM_NO_MIPMAP;
var context:Context3D = Starling.context;
var pma:Boolean = texture.premultipliedAlpha;
sRenderAlpha[0] = sRenderAlpha[1] = sRenderAlpha[2] = pma ? alpha : 1.0;
sRenderAlpha[3] = alpha;
if (context == null)
throw new MissingContextError();
mVertexBuffer.uploadFromVector(mVertexData.rawData, 0, mNumParticles * 4);
mIndexBuffer.uploadFromVector(mIndices, 0, mNumParticles * 6);
context.setBlendFactors(mBlendFactorSource, mBlendFactorDestination);
context.setTextureAt(0, mTexture.base);
context.setProgram(Starling.current.getProgram(program));
context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, support.mvpMatrix3D, true);
context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, sRenderAlpha, 1);
context.setVertexBufferAt(0, mVertexBuffer, VertexData.POSITION_OFFSET, Context3DVertexBufferFormat.FLOAT_2);
context.setVertexBufferAt(1, mVertexBuffer, VertexData.COLOR_OFFSET, Context3DVertexBufferFormat.FLOAT_4);
context.setVertexBufferAt(2, mVertexBuffer, VertexData.TEXCOORD_OFFSET, Context3DVertexBufferFormat.FLOAT_2);
context.drawTriangles(mIndexBuffer, 0, mNumParticles * 2);
context.setTextureAt(0, null);
context.setVertexBufferAt(0, null);
context.setVertexBufferAt(1, null);
context.setVertexBufferAt(2, null);
}
// program management
private static function registerPrograms():void
{
var target:Starling = Starling.current;
if (target.hasProgram(PROGRAM_MIPMAP))
return; // already registered
for each (var mipmap:Boolean in[true, false])
{
// create vertex and fragment programs - from assembly.
var programName:String = mipmap ? PROGRAM_MIPMAP : PROGRAM_NO_MIPMAP;
var textureOptions:String = "2d, clamp, linear, " + (mipmap ? "mipnearest" : "mipnone");
var vertexProgramCode:String = "m44 op, va0, vc0 \n" + // 4x4 matrix transform to output clipspace
"mul v0, va1, vc4 \n" + // multiply color with alpha and pass to fragment program
"mov v1, va2 \n"; // pass texture coordinates to fragment program
var fragmentProgramCode:String = "tex ft1, v1, fs0 <" + textureOptions + "> \n" + // sample texture 0
"mul oc, ft1, v0"; // multiply color with texel color
var vertexProgramAssembler:AGALMiniAssembler = new AGALMiniAssembler();
vertexProgramAssembler.assemble(Context3DProgramType.VERTEX, vertexProgramCode);
var fragmentProgramAssembler:AGALMiniAssembler = new AGALMiniAssembler();
fragmentProgramAssembler.assemble(Context3DProgramType.FRAGMENT, fragmentProgramCode);
target.registerProgram(programName, vertexProgramAssembler.agalcode, fragmentProgramAssembler.agalcode);
}
}
public function get capacity():int
{
return mVertexData.numVertices / 4;
}
public function get numParticles():int
{
return mNumParticles;
}
public function get maxCapacity():int
{
return mMaxCapacity;
}
public function set maxCapacity(value:int):void
{
mMaxCapacity = Math.min(8192, value);
}
public function get emissionRate():Number
{
return mEmissionRate;
}
public function set emissionRate(value:Number):void
{
mEmissionRate = value;
}
public function get emitterX():Number
{
return mEmitterX;
}
public function set emitterX(value:Number):void
{
mEmitterX = value;
}
public function get emitterY():Number
{
return mEmitterY;
}
public function set emitterY(value:Number):void
{
mEmitterY = value;
}
public function get blendFactorSource():String
{
return mBlendFactorSource;
}
public function set blendFactorSource(value:String):void
{
mBlendFactorSource = value;
}
public function get blendFactorDestination():String
{
return mBlendFactorDestination;
}
public function set blendFactorDestination(value:String):void
{
mBlendFactorDestination = value;
}
public function get texture():Texture
{
return mTexture;
}
}
import flash.display3D.Context3DBlendFactor;
import starling.textures.Texture;
import starling.utils.deg2rad;
class PDParticleSystem extends ParticleSystem
{
private const EMITTER_TYPE_GRAVITY:int = 0;
private const EMITTER_TYPE_RADIAL:int = 1;
// emitter configuration // .pex element name
private var mEmitterType:int; // emitterType
private var mEmitterXVariance:Number; // sourcePositionVariance x
private var mEmitterYVariance:Number; // sourcePositionVariance y
// particle configuration
private var mMaxNumParticles:int; // maxParticles
private var mLifespan:Number; // particleLifeSpan
private var mLifespanVariance:Number; // particleLifeSpanVariance
private var mStartSize:Number; // startParticleSize
private var mStartSizeVariance:Number; // startParticleSizeVariance
private var mEndSize:Number; // finishParticleSize
private var mEndSizeVariance:Number; // finishParticleSizeVariance
private var mEmitAngle:Number; // angle
private var mEmitAngleVariance:Number; // angleVariance
private var mStartRotation:Number; // rotationStart
private var mStartRotationVariance:Number; // rotationStartVariance
private var mEndRotation:Number; // rotationEnd
private var mEndRotationVariance:Number; // rotationEndVariance
// gravity configuration
private var mSpeed:Number; // speed
private var mSpeedVariance:Number; // speedVariance
private var mGravityX:Number; // gravity x
private var mGravityY:Number; // gravity y
private var mRadialAcceleration:Number; // radialAcceleration
private var mRadialAccelerationVariance:Number; // radialAccelerationVariance
private var mTangentialAcceleration:Number; // tangentialAcceleration
private var mTangentialAccelerationVariance:Number; // tangentialAccelerationVariance
// radial configuration
private var mMaxRadius:Number; // maxRadius
private var mMaxRadiusVariance:Number; // maxRadiusVariance
private var mMinRadius:Number; // minRadius
private var mRotatePerSecond:Number; // rotatePerSecond
private var mRotatePerSecondVariance:Number; // rotatePerSecondVariance
// color configuration
private var mStartColor:ColorArgb; // startColor
private var mStartColorVariance:ColorArgb; // startColorVariance
private var mEndColor:ColorArgb; // finishColor
private var mEndColorVariance:ColorArgb; // finishColorVariance
public function PDParticleSystem(config:XML, texture:Texture)
{
parseConfig(config);
var emissionRate:Number = mMaxNumParticles / mLifespan;
super(texture, emissionRate, mMaxNumParticles, mMaxNumParticles, mBlendFactorSource, mBlendFactorDestination);
mPremultipliedAlpha = false;
}
protected override function createParticle():Particle
{
return new PDParticle();
}
protected override function initParticle(aParticle:Particle):void
{
var particle:PDParticle = aParticle as PDParticle;
// for performance reasons, the random variances are calculated inline instead
// of calling a function
var lifespan:Number = mLifespan + mLifespanVariance * (Math.random() * 2.0 - 1.0);
if (lifespan <= 0.0)
return;
particle.currentTime = 0.0;
particle.totalTime = lifespan;
particle.x = mEmitterX + mEmitterXVariance * (Math.random() * 2.0 - 1.0);
particle.y = mEmitterY + mEmitterYVariance * (Math.random() * 2.0 - 1.0);
particle.startX = mEmitterX;
particle.startY = mEmitterY;
var angle:Number = mEmitAngle + mEmitAngleVariance * (Math.random() * 2.0 - 1.0);
var speed:Number = mSpeed + mSpeedVariance * (Math.random() * 2.0 - 1.0);
particle.velocityX = speed * Math.cos(angle);
particle.velocityY = speed * Math.sin(angle);
particle.emitRadius = mMaxRadius + mMaxRadiusVariance * (Math.random() * 2.0 - 1.0);
particle.emitRadiusDelta = mMaxRadius / lifespan;
particle.emitRotation = mEmitAngle + mEmitAngleVariance * (Math.random() * 2.0 - 1.0);
particle.emitRotationDelta = mRotatePerSecond + mRotatePerSecondVariance * (Math.random() * 2.0 - 1.0);
particle.radialAcceleration = mRadialAcceleration + mRadialAccelerationVariance * (Math.random() * 2.0 - 1.0);
particle.tangentialAcceleration = mTangentialAcceleration + mTangentialAccelerationVariance * (Math.random() * 2.0 - 1.0);
var startSize:Number = mStartSize + mStartSizeVariance * (Math.random() * 2.0 - 1.0);
var endSize:Number = mEndSize + mEndSizeVariance * (Math.random() * 2.0 - 1.0);
if (startSize < 0.1)
startSize = 0.1;
if (endSize < 0.1)
endSize = 0.1;
particle.scale = startSize / texture.width;
particle.scaleDelta = ((endSize - startSize) / lifespan) / texture.width;
// colors
var startColor:ColorArgb = particle.colorArgb;
var colorDelta:ColorArgb = particle.colorArgbDelta;
startColor.red = mStartColor.red;
startColor.green = mStartColor.green;
startColor.blue = mStartColor.blue;
startColor.alpha = mStartColor.alpha;
if (mStartColorVariance.red != 0)
startColor.red += mStartColorVariance.red * (Math.random() * 2.0 - 1.0);
if (mStartColorVariance.green != 0)
startColor.green += mStartColorVariance.green * (Math.random() * 2.0 - 1.0);
if (mStartColorVariance.blue != 0)
startColor.blue += mStartColorVariance.blue * (Math.random() * 2.0 - 1.0);
if (mStartColorVariance.alpha != 0)
startColor.alpha += mStartColorVariance.alpha * (Math.random() * 2.0 - 1.0);
var endColorRed:Number = mEndColor.red;
var endColorGreen:Number = mEndColor.green;
var endColorBlue:Number = mEndColor.blue;
var endColorAlpha:Number = mEndColor.alpha;
if (mEndColorVariance.red != 0)
endColorRed += mEndColorVariance.red * (Math.random() * 2.0 - 1.0);
if (mEndColorVariance.green != 0)
endColorGreen += mEndColorVariance.green * (Math.random() * 2.0 - 1.0);
if (mEndColorVariance.blue != 0)
endColorBlue += mEndColorVariance.blue * (Math.random() * 2.0 - 1.0);
if (mEndColorVariance.alpha != 0)
endColorAlpha += mEndColorVariance.alpha * (Math.random() * 2.0 - 1.0);
colorDelta.red = (endColorRed - startColor.red) / lifespan;
colorDelta.green = (endColorGreen - startColor.green) / lifespan;
colorDelta.blue = (endColorBlue - startColor.blue) / lifespan;
colorDelta.alpha = (endColorAlpha - startColor.alpha) / lifespan;
// rotation
var startRotation:Number = mStartRotation + mStartRotationVariance * (Math.random() * 2.0 - 1.0);
var endRotation:Number = mEndRotation + mEndRotationVariance * (Math.random() * 2.0 - 1.0);
particle.rotation = startRotation;
particle.rotationDelta = (endRotation - startRotation) / lifespan;
}
protected override function advanceParticle(aParticle:Particle, passedTime:Number):void
{
var particle:PDParticle = aParticle as PDParticle;
var restTime:Number = particle.totalTime - particle.currentTime;
passedTime = restTime > passedTime ? passedTime : restTime;
particle.currentTime += passedTime;
if (mEmitterType == EMITTER_TYPE_RADIAL)
{
particle.emitRotation += particle.emitRotationDelta * passedTime;
particle.emitRadius -= particle.emitRadiusDelta * passedTime;
particle.x = mEmitterX - Math.cos(particle.emitRotation) * particle.emitRadius;
particle.y = mEmitterY - Math.sin(particle.emitRotation) * particle.emitRadius;
if (particle.emitRadius < mMinRadius)
particle.currentTime = particle.totalTime;
}
else
{
var distanceX:Number = particle.x - particle.startX;
var distanceY:Number = particle.y - particle.startY;
var distanceScalar:Number = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if (distanceScalar < 0.01)
distanceScalar = 0.01;
var radialX:Number = distanceX / distanceScalar;
var radialY:Number = distanceY / distanceScalar;
var tangentialX:Number = radialX;
var tangentialY:Number = radialY;
radialX *= particle.radialAcceleration;
radialY *= particle.radialAcceleration;
var newY:Number = tangentialX;
tangentialX = -tangentialY * particle.tangentialAcceleration;
tangentialY = newY * particle.tangentialAcceleration;
particle.velocityX += passedTime * (mGravityX + radialX + tangentialX);
particle.velocityY += passedTime * (mGravityY + radialY + tangentialY);
particle.x += particle.velocityX * passedTime;
particle.y += particle.velocityY * passedTime;
}
particle.scale += particle.scaleDelta * passedTime;
particle.rotation += particle.rotationDelta * passedTime;
particle.colorArgb.red += particle.colorArgbDelta.red * passedTime;
particle.colorArgb.green += particle.colorArgbDelta.green * passedTime;
particle.colorArgb.blue += particle.colorArgbDelta.blue * passedTime;
particle.colorArgb.alpha += particle.colorArgbDelta.alpha * passedTime;
particle.color = particle.colorArgb.toRgb();
particle.alpha = particle.colorArgb.alpha;
}
private function updateEmissionRate():void
{
emissionRate = mMaxNumParticles / mLifespan;
}
private function parseConfig(config:XML):void
{
mEmitterXVariance = parseFloat(config.sourcePositionVariance.attribute("x"));
mEmitterYVariance = parseFloat(config.sourcePositionVariance.attribute("y"));
mGravityX = parseFloat(config.gravity.attribute("x"));
mGravityY = parseFloat(config.gravity.attribute("y"));
mEmitterType = getIntValue(config.emitterType);
mMaxNumParticles = getIntValue(config.maxParticles);
mLifespan = Math.max(0.01, getFloatValue(config.particleLifeSpan));
mLifespanVariance = getFloatValue(config.particleLifespanVariance);
mStartSize = getFloatValue(config.startParticleSize);
mStartSizeVariance = getFloatValue(config.startParticleSizeVariance);
mEndSize = getFloatValue(config.finishParticleSize);
mEndSizeVariance = getFloatValue(config.FinishParticleSizeVariance);
mEmitAngle = deg2rad(getFloatValue(config.angle));
mEmitAngleVariance = deg2rad(getFloatValue(config.angleVariance));
mStartRotation = deg2rad(getFloatValue(config.rotationStart));
mStartRotationVariance = deg2rad(getFloatValue(config.rotationStartVariance));
mEndRotation = deg2rad(getFloatValue(config.rotationEnd));
mEndRotationVariance = deg2rad(getFloatValue(config.rotationEndVariance));
mSpeed = getFloatValue(config.speed);
mSpeedVariance = getFloatValue(config.speedVariance);
mRadialAcceleration = getFloatValue(config.radialAcceleration);
mRadialAccelerationVariance = getFloatValue(config.radialAccelVariance);
mTangentialAcceleration = getFloatValue(config.tangentialAcceleration);
mTangentialAccelerationVariance = getFloatValue(config.tangentialAccelVariance);
mMaxRadius = getFloatValue(config.maxRadius);
mMaxRadiusVariance = getFloatValue(config.maxRadiusVariance);
mMinRadius = getFloatValue(config.minRadius);
mRotatePerSecond = deg2rad(getFloatValue(config.rotatePerSecond));
mRotatePerSecondVariance = deg2rad(getFloatValue(config.rotatePerSecondVariance));
mStartColor = getColor(config.startColor);
mStartColorVariance = getColor(config.startColorVariance);
mEndColor = getColor(config.finishColor);
mEndColorVariance = getColor(config.finishColorVariance);
mBlendFactorSource = getBlendFunc(config.blendFuncSource);
mBlendFactorDestination = getBlendFunc(config.blendFuncDestination);
function getIntValue(element:XMLList):int
{
return parseInt(element.attribute("value"));
}
function getFloatValue(element:XMLList):Number
{
return parseFloat(element.attribute("value"));
}
function getColor(element:XMLList):ColorArgb
{
var color:ColorArgb = new ColorArgb();
color.red = parseFloat(element.attribute("red"));
color.green = parseFloat(element.attribute("green"));
color.blue = parseFloat(element.attribute("blue"));
color.alpha = parseFloat(element.attribute("alpha"));
return color;
}
function getBlendFunc(element:XMLList):String
{
var value:int = getIntValue(element);
switch (value)
{
case 0:
return Context3DBlendFactor.ZERO;
break;
case 1:
return Context3DBlendFactor.ONE;
break;
case 0x300:
return Context3DBlendFactor.SOURCE_COLOR;
break;
case 0x301:
return Context3DBlendFactor.ONE_MINUS_SOURCE_COLOR;
break;
case 0x302:
return Context3DBlendFactor.SOURCE_ALPHA;
break;
case 0x303:
return Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
break;
case 0x304:
return Context3DBlendFactor.DESTINATION_ALPHA;
break;
case 0x305:
return Context3DBlendFactor.ONE_MINUS_DESTINATION_ALPHA;
break;
case 0x306:
return Context3DBlendFactor.DESTINATION_COLOR;
break;
case 0x307:
return Context3DBlendFactor.ONE_MINUS_DESTINATION_COLOR;
break;
default:
throw new ArgumentError("unsupported blending function: " + value);
}
}
}
public function get emitterType():int
{
return mEmitterType;
}
public function set emitterType(value:int):void
{
mEmitterType = value;
}
public function get emitterXVariance():Number
{
return mEmitterXVariance;
}
public function set emitterXVariance(value:Number):void
{
mEmitterXVariance = value;
}
public function get emitterYVariance():Number
{
return mEmitterYVariance;
}
public function set emitterYVariance(value:Number):void
{
mEmitterYVariance = value;
}
public function get maxNumParticles():int
{
return mMaxNumParticles;
}
public function set maxNumParticles(value:int):void
{
maxCapacity = value;
mMaxNumParticles = maxCapacity;
updateEmissionRate();
}
public function get lifespan():Number
{
return mLifespan;
}
public function set lifespan(value:Number):void
{
mLifespan = Math.max(0.01, value);
updateEmissionRate();
}
public function get lifespanVariance():Number
{
return mLifespanVariance;
}
public function set lifespanVariance(value:Number):void
{
mLifespanVariance = value;
}
public function get startSize():Number
{
return mStartSize;
}
public function set startSize(value:Number):void
{
mStartSize = value;
}
public function get startSizeVariance():Number
{
return mStartSizeVariance;
}
public function set startSizeVariance(value:Number):void
{
mStartSizeVariance = value;
}
public function get endSize():Number
{
return mEndSize;
}
public function set endSize(value:Number):void
{
mEndSize = value;
}
public function get endSizeVariance():Number
{
return mEndSizeVariance;
}
public function set endSizeVariance(value:Number):void
{
mEndSizeVariance = value;
}
public function get emitAngle():Number
{
return mEmitAngle;
}
public function set emitAngle(value:Number):void
{
mEmitAngle = value;
}
public function get emitAngleVariance():Number
{
return mEmitAngleVariance;
}
public function set emitAngleVariance(value:Number):void
{
mEmitAngleVariance = value;
}
public function get startRotation():Number
{
return mStartRotation;
}
public function set startRotation(value:Number):void
{
mStartRotation = value;
}
public function get startRotationVariance():Number
{
return mStartRotationVariance;
}
public function set startRotationVariance(value:Number):void
{
mStartRotationVariance = value;
}
public function get endRotation():Number
{
return mEndRotation;
}
public function set endRotation(value:Number):void
{
mEndRotation = value;
}
public function get endRotationVariance():Number
{
return mEndRotationVariance;
}
public function set endRotationVariance(value:Number):void
{
mEndRotationVariance = value;
}
public function get speed():Number
{
return mSpeed;
}
public function set speed(value:Number):void
{
mSpeed = value;
}
public function get speedVariance():Number
{
return mSpeedVariance;
}
public function set speedVariance(value:Number):void
{
mSpeedVariance = value;
}
public function get gravityX():Number
{
return mGravityX;
}
public function set gravityX(value:Number):void
{
mGravityX = value;
}
public function get gravityY():Number
{
return mGravityY;
}
public function set gravityY(value:Number):void
{
mGravityY = value;
}
public function get radialAcceleration():Number
{
return mRadialAcceleration;
}
public function set radialAcceleration(value:Number):void
{
mRadialAcceleration = value;
}
public function get radialAccelerationVariance():Number
{
return mRadialAccelerationVariance;
}
public function set radialAccelerationVariance(value:Number):void
{
mRadialAccelerationVariance = value;
}
public function get tangentialAcceleration():Number
{
return mTangentialAcceleration;
}
public function set tangentialAcceleration(value:Number):void
{
mTangentialAcceleration = value;
}
public function get tangentialAccelerationVariance():Number
{
return mTangentialAccelerationVariance;
}
public function set tangentialAccelerationVariance(value:Number):void
{
mTangentialAccelerationVariance = value;
}
public function get maxRadius():Number
{
return mMaxRadius;
}
public function set maxRadius(value:Number):void
{
mMaxRadius = value;
}
public function get maxRadiusVariance():Number
{
return mMaxRadiusVariance;
}
public function set maxRadiusVariance(value:Number):void
{
mMaxRadiusVariance = value;
}
public function get minRadius():Number
{
return mMinRadius;
}
public function set minRadius(value:Number):void
{
mMinRadius = value;
}
public function get rotatePerSecond():Number
{
return mRotatePerSecond;
}
public function set rotatePerSecond(value:Number):void
{
mRotatePerSecond = value;
}
public function get rotatePerSecondVariance():Number
{
return mRotatePerSecondVariance;
}
public function set rotatePerSecondVariance(value:Number):void
{
mRotatePerSecondVariance = value;
}
public function get startColor():ColorArgb
{
return mStartColor;
}
public function set startColor(value:ColorArgb):void
{
mStartColor = value;
}
public function get startColorVariance():ColorArgb
{
return mStartColorVariance;
}
public function set startColorVariance(value:ColorArgb):void
{
mStartColorVariance = value;
}
public function get endColor():ColorArgb
{
return mEndColor;
}
public function set endColor(value:ColorArgb):void
{
mEndColor = value;
}
public function get endColorVariance():ColorArgb
{
return mEndColorVariance;
}
public function set endColorVariance(value:ColorArgb):void
{
mEndColorVariance = value;
}
}
import starling.textures.Texture;
class ParticleDesignerPS extends PDParticleSystem
{
private static var sDeprecationNotified:Boolean = false;
public function ParticleDesignerPS(config:XML, texture:Texture)
{
if (!sDeprecationNotified)
{
sDeprecationNotified = true;
trace("[Starling] The class 'ParticleDesignerPS' is deprecated. " + "Please use 'PDParticleSystem' instead.");
}
super(config, texture);
}
}