In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

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);
    }
}