Math and (Quasi) Physics in Action Script 3

3D particles (1) - depth of field and additive blending

A practice of making a 3D effect without using any libraries.
The particles are blured as they get close to the camera and their trajectories are drawn with additive blending.

Main.as

´╗┐package {
  import flash.display.*;
  import flash.events.*;
  import flash.filters.*;
  import flash.geom.*;
  public class Main extends Sprite {
    private var particles:Array = new Array();
    private var _focalLength:Number = 350;
    private var camX:Number = 0,camY:Number = -100,camZ:Number = 0;
    private var rX:Number = 0,rY:Number = 0,rZ:Number = 0;
    private var cx:Number,cy:Number;
    private var canvas:Bitmap;
    private var angle = 0;
    private var images = new Array();

    public function Main() {
      var bg = new Sprite();
      addChild(bg);
      bg.graphics.beginFill(0x000000);
      bg.graphics.drawRect(0,0,stage.stageWidth, stage.stageHeight);
      bg.graphics.endFill();
      addChild(canvas = new Bitmap(new BitmapData(stage.stageWidth,stage.stageHeight,true,0x000000)));

      cx = stage.stageWidth / 2;
      cy = stage.stageWidth / 2;

      var ss:Sprite = new Sprite();
      var s:Sprite = new Sprite();
      s.addChild(ss);
      ss.graphics.beginGradientFill(GradientType.RADIAL,[0xffff00,0x660000],[0.5,0.1],[50,255]);
      ss.graphics.drawCircle(40,40,100);
      ss.graphics.endFill();
      ss.x = ss.y = 40;
      var bd:BitmapData;
      var b:Bitmap;
      var blur:BlurFilter;
      for (var i:uint = 1; i < 100; i ++) {
        ss.scaleX = i / 7 / 50;
        ss.scaleY = i / 7 / 50;
        if (i > 20) {
          blur = new BlurFilter((i - 20) /4,(i -20) / 4);
          ss.filters = [blur];
        }
        bd = new BitmapData(80,80,true,0x00000000);
        bd.draw(s);
        images.push(bd);
      }
      particles.push(new Particle());
      addEventListener(Event.ENTER_FRAME, h_enterFrame);
    }

    private function h_enterFrame(evt:Event):void {
      if (Math.random() < 1) {
        particles.push(new Particle());
      }
      draw();
    }

    private function draw():void {

      var i:uint,j:uint;
      var tParticles:Array = new Array();
      var p:Object;
      var p3d:Object;
      var p2d:Object;
      var mtx:Matrix;
      var filter:BitmapFilter;

      var bmd:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true,0x000000);
      var tz:uint = 0;
      for (j = 0; j < 3; j ++) {
        angle++;
        for (i = particles.length - 1; i > 0; i --) {
          particles[i].update();
          p = new Object();
          p.x = particles[i].x;
          p.y = particles[i].y;
          p.z = particles[i].z;
          //p = rotateX(p,60);
          p = rotateY(p,angle);
          //p = rotateZ(p, 0);

          p.x -= camX;
          p.y-=camY;
          p.z-=camZ;
          tParticles.push(p);
          if (particles[i].life<0) {
            particles.splice(0,1);
          }
        }

        tParticles.sortOn("z", Array.NUMERIC | Array.DESCENDING);

        for (i = 0; i < tParticles.length; i ++) {
          p3d = tParticles[i]
          if (p3d.z < -_focalLength) {
          continue
          }
          p2d=p3Dto2D(p3d,_focalLength);
          mtx = new Matrix();
          mtx.translate(p2d.x - 30 + cx, p2d.y - 30 + cy);
          tz=Math.floor(p2d.scale*20);
          if (tz>98) {
            tz=98;
          }
          bmd.draw(images[tz],mtx);
        }
      }
filter = new ColorMatrixFilter([0.8,0,0,0,0,
      0,0.8,0,0,0,
      0,0,0.8,0,0,
      0,0,0,0.99,0]);
      canvas.bitmapData.applyFilter(canvas.bitmapData,new Rectangle(0,0,stage.stageWidth,stage.stageHeight),new Point(),filter);
      

      
      canvas.bitmapData.draw(bmd,new Matrix(), new ColorTransform(), BlendMode.ADD);
      //canvas.bitmapData=bmd;
    }


    private function rotateX(p3d:Object, angle:Number):Object {
      var p:Object = new Object();
      angle=angle/180*Math.PI;
      p.x=p3d.x;
      p.y=Math.cos(angle)*p3d.y-Math.sin(angle)*p3d.z;
      p.z=Math.cos(angle)*p3d.z+Math.sin(angle)*p3d.y;
      return p;
    }

    private function rotateY(p3d:Object, angle:Number):Object {
      var p:Object = new Object();
      angle=angle/180*Math.PI;
      p.x=Math.cos(angle)*p3d.x-Math.sin(angle)*p3d.z;
      p.y=p3d.y;
      p.z=Math.cos(angle)*p3d.z+Math.sin(angle)*p3d.x;
      return p;
    }

    private function rotateZ(p3d:Object, angle:Number):Object {
      var p:Object = new Object();
      angle=angle/180*Math.PI;
      p.x=Math.cos(angle)*p3d.x-Math.sin(angle)*p3d.y;
      p.y=Math.cos(angle)*p3d.y+Math.sin(angle)*p3d.x;
      p.z=p3d.z;
      return p;
    }

    private function p3Dto2D(p3d:Object, _fl:Number):Object {
      var scale:Number;
      var p:Object = new Object();
      scale = _fl / (_fl + p3d.z);
      p.x=p3d.x*scale;
      p.y=p3d.y*scale;
      p.scale=scale;
      return p;
    }


  }
}

class Particle {
  import flash.display.*;
  public var x:Number=0,y:Number=0,z:Number=0;
  public var vx:Number=0,vy:Number=0,vz:Number=0;
  public var life:int=200;
  function Particle() {
    x=Math.random()*100;
    y=- Math.random()*100;
    z=Math.random()*100+100;
    vx=Math.random()*5-2.5;
    vy=Math.random()*-4 -4;
    vz=Math.random()*-1-1;
  }

  function update():void {
    if (y>=0) {
      vy*=-0.8;
      if (Math.abs(vy)<2) {
        vy=0;
      }
      y=0;
    } else {
      vy+=0.1;
    }
    x+=vx;
    y+=vy;
    z+=vz;
    life--;
    if (z>1000) {
      life=0;
    }
  }
}