Forked from: yonatan's lighting test diff:1 forked from: lighting test ChrisPaul forked:0favorite:0lines:478license : MIT License modified : 2012-03-12 22:13:54 Embed Tweet // forked from yonatan's lighting test package { import flash.display.*; import flash.events.*; import flash.geom.*; import flash.utils.*; import flash.net.*; import com.bit101.components.*; import org.si.sion.utils.soundloader.*; [SWF(width="465", height="465", frameRate="60", backgroundColor="0")] public class main extends Sprite { private const LINE_WIDTH_FACTOR:Number = 0.5; private const BRANCH_SCALE:Number = 0.74; private const VS:int = 350; // viewport size private const HVS:int = VS/2; private const SS:int = 465; // stage size private const HSS:int = SS/2; private const INIT_SIZE:Number = 120; // Minimal Gradient Editor (SWF素材) - http://wonderfl.net/c/6i8T private var GRADIENT_EDITOR_URL:String = "http://swf.wonderfl.net/swf/usercode/2/2c/2c1a/2c1a167b1a4db168e952e96df4e3d642544eb624.swf"; private var sigl:SiGLCore = new SiGLCore(VS, VS); private var cbDrawAll:CheckBox; private var camSpeed:Number = 3; private var cam:SiGLMatrix = new SiGLMatrix; private var lastFrameTime:int = 0; private var minThickness:Number = 2; private var targetFPS:int = 24; private var maskAlpha:Number = 1; private var mt:Label; private var vl:VolumetricPointLight; private var intensity:Number = 2; private var light:Vector3D = new Vector3D; private var container:Sprite = new Sprite; private var mge:*; private var lockOnSun:Boolean = false; public function main() { // GRADIENT_EDITOR_URL = "mge.swf" stage.quality = "medium"; stage.scaleMode = "noScale"; x = y = HSS; stage.addChild(container); var loader:SoundLoader = new SoundLoader(); loader.setURL(new URLRequest(GRADIENT_EDITOR_URL), "mge", null, true); loader.addEventListener(Event.COMPLETE, onLoaded); loader.loadAll(); } private function onLoaded(e:Event):void { vl = new VolumetricPointLight(VS, VS, this, [0xffffff, 0xffffff, 0xffcc88, 0x884422], [1, 1, 0.8, 0], [0, 4, 5, 120]); vl.scale = 2; vl.intensity = 2; vl.colorIntegrity = true; vl.setBufferSize(0x1000); vl.startRendering(); container.addChild(vl); light.x = 0; light.y = -20000; light.z = -100000; var MGE:Class = SoundLoader(e.target).hash["mge"].getClass(); mge = new MGE(stage, 5, 36, "light gradient:", {color: vl._colors, alpha: vl._alphas, ratio: vl._ratios}); //sigl.fieldOfView = 45; addEventListener("enterFrame", onEnterFrame); var uix:int = 5, uiy:int = 0; // var uix:int = -HSS+5, uiy:int = -HSS+5; var that:main = this; function _slider(txt:String, obj:Object, prop:String, min:Number, max:Number, val:Number):void { var s:HUISlider = new HUISlider(that.stage, uix, uiy, txt, function(e:Event):void { obj[prop] = e.target.value; }); s.minimum = min; s.maximum = max; obj[prop] = s.value = val; uiy += 18; } function _cb(txt:String, obj:Object, prop:String, val:Boolean):void { var s:CheckBox = new CheckBox(that.stage, uix, uiy+3, txt, function(e:Event):void { obj[prop] = e.target.selected; }); obj[prop] = s.selected = val; uiy += 18; } // cbDrawAll = new CheckBox(this, uix, uiy, "Draw all computed branches (incl. clipped ones)"); uiy += 18; _slider("Target FPS", this, "targetFPS", 6, 40, targetFPS); // _slider("Mask alpha", "maskAlpha", 0, 1, 1); _slider("cam speed", this, "camSpeed", 0.1, 10, camSpeed); uiy=0; uix=190; _slider("light intensity", this, "intensity", 0.5, 10, vl.intensity); _slider("light scale", vl, "scale", 1, 4, vl.scale); _cb("colorIntegrity", vl, "colorIntegrity", vl.colorIntegrity); uiy = 36; uix = 300; _cb("lock cam on sun", this, "lockOnSun", false); new FPSMeter(stage, SS-100, 0); mt = new Label(stage, SS-100, 18); } private function onEnterFrame(e:Event):void { var t:int = getTimer(); var renderTime:int = t - lastFrameTime; lastFrameTime = t; var timeDiff:Number = Math.abs(renderTime - 1000/targetFPS); if(timeDiff > 300/targetFPS) { // don't adjust unless we have to (reduces flicker) minThickness += (renderTime > 1000/targetFPS) ? 0.1 : -0.1; if(minThickness < 1) minThickness = 1; } mt.text = "Min. thickness: " + minThickness.toFixed(2); graphics.clear(); var cx:Number = Math.sin(t/(56789/camSpeed)) * 1550; var cz:Number = Math.cos(t/(45678/camSpeed)) * 1550; var cy:Number = Math.cos(t/(34567/camSpeed)) * 500 - 500; var tw:Number = lockOnSun ? 1 : 0.5+0.5*Math.cos(t/(27890/camSpeed)); var twp:Number = Math.pow(tw, 6); var tx:Number = light.x * twp; var ty:Number = light.y * twp + (-INIT_SIZE/BRANCH_SCALE) * (1-twp); var tz:Number = light.z * twp; cy = cy * (1-tw) + tw * -INIT_SIZE; cam.id().prependTranslation(cx, cy, cz); cam.lookAt(cx,cy,cz, tx,ty,tz, 0,1,0, 1); sigl.setCameraMatrix(cam); for(var x:int = -4800; x <= 4800; x += 300) { for(var z:int = -4800; z <= 4800; z += 300) { sigl.push().t(x, 0, z).r(x+z, Vector3D.Y_AXIS); render(INIT_SIZE); sigl.pop(); } } var pl:Vector3D = new Vector3D; sigl.projectionMatrix.transform(light, pl); pl.w = pl.z/sigl.focalLength; pl.x /= pl.w; pl.y /= pl.w; vl.srcX = pl.x + HSS; vl.srcY = pl.y + HSS; //vl.intensity = (pl.z > 0 && pl.x > -SS && pl.x < SS && pl.y > -SS && pl.y < SS) ? 2 : 0; vl.intensity = Math.min(intensity, Math.min(pl.z, Math.abs(pl.x - VS), Math.abs(pl.y - VS)) / 100); if(vl.intensity < 0) vl.intensity = 0; vl.emission.visible = vl.intensity > 0; container.graphics.clear(); container.graphics.lineStyle(); container.graphics.beginFill(0x8497b4);//0x4488cc);//0xffeedd, maskAlpha); container.graphics.drawRect(0, 0, VS, VS); container.graphics.endFill(); //container.scrollRect = new Rectangle(0, 0, VS, VS); this.x = this.y = HVS;//HSS-(SS-VS); container.x = container.y = HSS-HVS; } private var p1:Vector3D = new Vector3D, p2:Vector3D = new Vector3D; private function render(size:Number):void { function projxy(v:Vector3D):void { sigl.projectionMatrix.transform(sigl.matrix.position, v); v.w = v.z/sigl.focalLength; v.x /= v.w; v.y /= v.w; // v.project(); } sigl.push(); projxy(p1); var limit:Number = size / (1 - BRANCH_SCALE); var xl:Number = HVS+limit*2/p1.w; // why *2 ??? var yl:Number = HVS+limit*2/p1.w; if(p1.z > sigl.zNear-limit && p1.x > -xl && p1.x < xl && p1.y > -yl && p1.y < yl ) { size *= BRANCH_SCALE; sigl.t(0, -size, 0); projxy(p2); var thickness:Number = size * LINE_WIDTH_FACTOR / ((p1.w+p2.w)*0.5); // crude clipping var cs:Number = HVS + thickness * 0.5; var isClipped:Boolean = (p1.x < -cs && p2.x < -cs) || (p1.x > cs && p2.x > cs) || (p1.y < -cs && p2.y < -cs) || (p1.y > cs && p2.y > cs) || p1.z < 0 || p2.z < 0; if(thickness > minThickness) { if((cbDrawAll && cbDrawAll.selected) || !isClipped) { graphics.lineStyle(thickness, 0, (thickness-minThickness), false, "normal", "none"); graphics.moveTo(p1.x, p1.y); graphics.lineTo(p2.x, p2.y); } sigl.r(120, Vector3D.Y_AXIS).r(-30, Vector3D.X_AXIS); render(size); sigl.r(70, Vector3D.X_AXIS); render(size); } } sigl.pop(); } } } import flash.display.*; import flash.events.*; import flash.filters.*; import flash.geom.*; // useful stuff by keim_at_Si (i think i copied it from the boolean crystals demo) // modifications: // - dest arg in SiGLMatrix transform() function // - reuse objects on SiGLMatrix stack in push, pop, rem (rem might be broken now) // - focalLength, zNear, zFar getters in SiGLCore /** SiGLCore provides basic matrix operations. */ class SiGLCore { // variables ---------------------------------------- public var modelViewMatrix:SiGLMatrix = new SiGLMatrix(), projectionMatrix:SiGLMatrix = new SiGLMatrix(); public var viewWidth:Number, viewHeight:Number, pointSpriteFieldScale:Point = new Point(); public var defaultCameraMatrix:SiGLMatrix = new SiGLMatrix(), matrix:SiGLMatrix = modelViewMatrix; private var _mvpMatrix:SiGLMatrix = new SiGLMatrix(), _mvpdir:Boolean, _2d:Number, _2r:Number; private var _mag:Number, _zNear:Number, _zFar:Number, _fieldOfView:Number, _fl:Number, _alignTopLeft:Boolean = false; // properties ---------------------------------------- public function get modelViewProjectionMatrix() : SiGLMatrix { if (_mvpdir) { _mvpMatrix.copyFrom(projectionMatrix); _mvpMatrix.prepend(modelViewMatrix); _mvpdir = false; } return _mvpMatrix; } public function get focalLength() : Number { return _fl; } public function get zNear() : Number { return _zNear; } public function get zFar() : Number { return _zFar; } public function get align() : String { return (_alignTopLeft) ? "topLeft" : "center"; } public function set align(mode:String) : void { _alignTopLeft = (mode == "topLeft"); _updateProjectionMatrix(); } public function get matrixMode() : String { return (matrix === projectionMatrix) ? "projection" : "modelView"; } public function set matrixMode(mode:String) : void { matrix = (mode == "projection") ? projectionMatrix : modelViewMatrix; } public function get angleMode() : String { return (_2r == 1) ? "radian" : "degree"; } public function set angleMode(mode:String) : void { _2d = (mode == "radian") ? 57.29577951308232 : 1; _2r = (mode == "radian") ? 1 : 0.017453292519943295; } public function get fieldOfView() : Number { return _fieldOfView / _2r; } public function set fieldOfView(fov:Number) : void { _fieldOfView = fov * _2r; _updateProjectionMatrix(); } public function get magnification() : Number { return _mag; } public function set magnification(mag:Number) : void { _mag = mag; _updateProjectionMatrix(); } // constructor ---------------------------------------- function SiGLCore(width:Number=1, height:Number=1) { viewWidth = width; viewHeight = height; angleMode = "degree"; _mag = 1; _zNear = -1000; _zFar = 200; modelViewMatrix.identity(); _mvpdir = true; this.fieldOfView = 60; } // matrix operations ---------------------------------------- public function forceUpdateMatrix() : SiGLCore { _mvpdir = true; return this; } public function setZRange(zNear:Number=-100, zFar:Number=100) : SiGLCore { _zNear = zNear; _zFar = zFar; _updateProjectionMatrix(); return this; } public function clear() : SiGLCore { matrix.clear(); _mvpdir = true; return this; } public function id() : SiGLCore { matrix.id(); _mvpdir = true; return this; } public function push() : SiGLCore { matrix.push(); return this; } public function pop() : SiGLCore { matrix.pop(); _mvpdir = true; return this; } public function rem() : SiGLCore { matrix.rem(); _mvpdir = true; return this; } public function r(a:Number, axis:Vector3D, pivot:Vector3D = null) : SiGLCore { matrix.prependRotation(a*_2d, axis, pivot); matrix._invdir = _mvpdir = true; return this; } public function s(x:Number, y:Number, z:Number=1) : SiGLCore { matrix.prependScale(x, y, z); matrix._invdir = _mvpdir = true; return this; } public function t(x:Number, y:Number, z:Number=0) : SiGLCore { matrix.prependTranslation(x, y, z); matrix._invdir = _mvpdir = true; return this; } public function m(mat:Matrix3D) : SiGLCore { matrix.prepend(mat); matrix._invdir = _mvpdir = true; return this; } public function re(x:Number, y:Number, z:Number) : SiGLCore { matrix.prependRotationXYZ(x*_2r, y*_2r, z*_2r); _mvpdir = true; return this; } public function setCameraMatrix(mat:Matrix3D=null) : SiGLCore { projectionMatrix.rem().prepend(mat || defaultCameraMatrix); _mvpdir = true; return this; } private function _updateProjectionMatrix() : void { var wh:Number = viewWidth / viewHeight, rev:Number = (_alignTopLeft)?-1:1; _fl = (viewHeight * 0.5) / Math.tan(_fieldOfView * 0.5); if (_zNear <= -_fl) _zNear = -_fl + 0.001; projectionMatrix.clear().perspectiveFieldOfView(_fieldOfView, wh, _zNear+_fl, _zFar+_fl, -1); pointSpriteFieldScale.setTo(projectionMatrix.rawData[0] * _fl, projectionMatrix.rawData[5] * _fl); projectionMatrix.push(); defaultCameraMatrix.identity(); defaultCameraMatrix.prependTranslation(0, 0, -_fl); if (_alignTopLeft) defaultCameraMatrix.prependTranslation(viewWidth* 0.5, -viewHeight * 0.5, 0); defaultCameraMatrix.prependScale(_mag, _mag * rev, _mag * rev); setCameraMatrix(); } } /** SiGLMatrix is extention of Matrix3D with push/pop operation */ class SiGLMatrix extends Matrix3D { internal var _top:int = 0, _invdir:Boolean = false, _inv:Matrix3D = new Matrix3D(), _stac:Vector.<Matrix3D> = new Vector.<Matrix3D>(); static private var _tv:Vector.<Number> = new Vector.<Number>(16, true), _tm:Matrix3D = new Matrix3D(); static private var _in:Vector.<Number> = new Vector.<Number>(4, true), _out:Vector.<Number> = new Vector.<Number>(4, true); public function get inverted() : Matrix3D { if (_invdir) { _inv.copyFrom(this); _inv.invert(); _invdir = false; } return _inv; } public function forceUpdateInvertedMatrix() : SiGLMatrix { _invdir=true; return this; } public function clear() : SiGLMatrix { _top = 0; return id(); } public function id() : SiGLMatrix { identity(); _inv.identity(); return this; } public function push() : SiGLMatrix { if(_stac.length == _top) _stac.push(new Matrix3D()); _stac[_top++].copyFrom(this); return this; } public function pop() : SiGLMatrix { this.copyFrom(_stac[--_top]); _invdir=true; return this; } public function rem() : SiGLMatrix { this.copyFrom(_stac[_top-1]); _invdir=true; return this; } public function prependRotationXYZ(rx:Number, ry:Number, rz:Number) : SiGLMatrix { var sx:Number = Math.sin(rx), sy:Number = Math.sin(ry), sz:Number = Math.sin(rz), cx:Number = Math.cos(rx), cy:Number = Math.cos(ry), cz:Number = Math.cos(rz); _tv[0] = cz*cy; _tv[1] = sz*cy; _tv[2] = -sy; _tv[4] = -sz*cx+cz*sy*sx; _tv[5] = cz*cx+sz*sy*sx; _tv[6] = cy*sx; _tv[8] = sz*sx+cz*sy*cx; _tv[9] = -cz*sx+sz*sy*cx; _tv[10] = cy*cx; _tv[14] = _tv[13] = _tv[12] = _tv[11] = _tv[7] = _tv[3] = 0; _tv[15] = 1; _tm.copyRawDataFrom(_tv); prepend(_tm); _invdir=true; return this; } public function lookAt(cx:Number, cy:Number, cz:Number, tx:Number=0, ty:Number=0, tz:Number=0, ux:Number=0, uy:Number=1, uz:Number=0, w:Number=0) : SiGLMatrix { var dx:Number=tx-cx, dy:Number=ty-cy, dz:Number=tz-cz, dl:Number=-1/Math.sqrt(dx*dx+dy*dy+dz*dz), rx:Number=dy*uz-dz*uy, ry:Number=dz*ux-dx*uz, rz:Number=dx*uy-dy*ux, rl:Number= 1/Math.sqrt(rx*rx+ry*ry+rz*rz); _tv[0] = (rx*=rl); _tv[4] = (ry*=rl); _tv[8] = (rz*=rl); _tv[12] = -(cx*rx+cy*ry+cz*rz) * w; _tv[2] = (dx*=dl); _tv[6] = (dy*=dl); _tv[10] = (dz*=dl); _tv[14] = -(cx*dx+cy*dy+cz*dz) * w; _tv[1] = (ux=dy*rz-dz*ry); _tv[5] = (uy=dz*rx-dx*rz); _tv[9] = (uz=dx*ry-dy*rx); _tv[13] = -(cx*ux+cy*uy+cz*uz) * w; _tv[3] = _tv[7] = _tv[11] = 0; _tv[15] = 1; copyRawDataFrom(_tv); _invdir=true; return this; } public function perspectiveFieldOfView(fieldOfViewY:Number, aspectRatio:Number, zNear:Number, zFar:Number, lh:Number=1.0) : void { var yScale:Number = 1.0 / Math.tan(fieldOfViewY * 0.5), xScale:Number = yScale / aspectRatio; this.copyRawDataFrom(Vector.<Number>([xScale,0,0,0,0,yScale,0,0,0,0,zFar/(zFar-zNear)*lh,lh,0,0,(zNear*zFar)/(zNear-zFar),0])); } public function transform(vector:Vector3D, dest:Vector3D = null) : Vector3D { if(!dest) dest = vector; _in[0] = vector.x; _in[1] = vector.y; _in[2] = vector.z; _in[3] = vector.w; transformVectors(_in, _out); dest.setTo(_out[0], _out[1], _out[2]); dest.w = _out[3]; return dest; } } // volumetrics ------------------------------------------------------------------------------ class EffectContainer extends Sprite { public var blur:Boolean = false; public var colorIntegrity:Boolean = false; public var intensity:Number = 4; public var passes:uint = 6; public var rasterQuality:String = null; public var scale:Number = 2; public var smoothing:Boolean = true; public var srcX:Number; public var srcY:Number; protected var _blurFilter:BlurFilter = new BlurFilter(2, 2); protected var _emission:DisplayObject; protected var _occlusion:DisplayObject; protected var _ct:ColorTransform = new ColorTransform; protected var _halve:ColorTransform = new ColorTransform(0.5, 0.5, 0.5); protected var _occlusionLoResBmd:BitmapData; protected var _occlusionLoResBmp:Bitmap; protected var _baseBmd:BitmapData; protected var _bufferBmd:BitmapData; protected var _lightBmp:Bitmap = new Bitmap; protected var _bufferSize:uint = 0x8000; protected var _bufferWidth:uint; protected var _bufferHeight:uint; protected var _viewportWidth:uint; protected var _viewportHeight:uint; protected var _mtx:Matrix = new Matrix; public function EffectContainer(width:uint, height:uint, emission:DisplayObject, occlusion:DisplayObject = null) { if(!emission) throw(new Error("emission DisplayObject must not be null.")); addChild(_emission = emission); if(occlusion) addChild(_occlusion = occlusion); setViewportSize(width, height); _lightBmp.blendMode = BlendMode.ADD; addChild(_lightBmp); srcX = width / 2; srcY = height / 2; } public function setViewportSize(width:uint, height:uint):void { _viewportWidth = width; _viewportHeight = height; scrollRect = new Rectangle(0, 0, width, height); _updateBuffers(); } public function setBufferSize(size:uint):void { _bufferSize = size; _updateBuffers(); } protected function _updateBuffers():void { var aspect:Number = _viewportWidth / _viewportHeight; _bufferHeight = Math.max(1, Math.sqrt(_bufferSize / aspect)); _bufferWidth = Math.max(1, _bufferHeight * aspect); dispose(); _baseBmd = new BitmapData(_bufferWidth, _bufferHeight, false, 0); _bufferBmd = new BitmapData(_bufferWidth, _bufferHeight, false, 0); _occlusionLoResBmd = new BitmapData(_bufferWidth, _bufferHeight, true, 0); _occlusionLoResBmp = new Bitmap(_occlusionLoResBmd); } public function render(e:Event = null):void { if(!(_lightBmp.visible = intensity > 0)) return; var savedQuality:String = stage.quality; if(rasterQuality) stage.quality = rasterQuality; var mul:Number = colorIntegrity ? intensity : intensity/(1<<passes); _ct.redMultiplier = _ct.greenMultiplier = _ct.blueMultiplier = mul; _drawLoResEmission(); if(_occlusion) _eraseLoResOcclusion(); if(rasterQuality) stage.quality = savedQuality; var s:Number = 1 + (scale-1) / (1 << passes); var tx:Number = srcX/_viewportWidth*_bufferWidth; var ty:Number = srcY/_viewportHeight*_bufferHeight; _mtx.identity(); _mtx.translate(-tx, -ty); _mtx.scale(s, s); _mtx.translate(tx, ty); _lightBmp.bitmapData = _applyEffect(_baseBmd, _bufferBmd, _mtx, passes); _lightBmp.width = _viewportWidth; _lightBmp.height = _viewportHeight; _lightBmp.smoothing = smoothing; } protected function _drawLoResEmission():void { _copyMatrix(_emission.transform.matrix, _mtx); _mtx.scale(_bufferWidth / _viewportWidth, _bufferHeight / _viewportHeight); _baseBmd.fillRect(_baseBmd.rect, 0); _baseBmd.draw(_emission, _mtx, colorIntegrity ? null : _ct); } protected function _eraseLoResOcclusion():void { _occlusionLoResBmd.fillRect(_occlusionLoResBmd.rect, 0); _copyMatrix(_occlusion.transform.matrix, _mtx); _mtx.scale(_bufferWidth / _viewportWidth, _bufferHeight / _viewportHeight); _occlusionLoResBmd.draw(_occlusion, _mtx); _baseBmd.draw(_occlusionLoResBmp, null, null, BlendMode.ERASE); } public function startRendering():void { addEventListener(Event.ENTER_FRAME, render); } public function stopRendering():void { removeEventListener(Event.ENTER_FRAME, render); } protected function _applyEffect(src:BitmapData, buffer:BitmapData, mtx:Matrix, passes:uint):BitmapData { var tmp:BitmapData; while(passes--) { if(colorIntegrity) src.colorTransform(src.rect, _halve); buffer.copyPixels(src, src.rect, src.rect.topLeft); buffer.draw(src, mtx, null, BlendMode.ADD, null, true); mtx.concat(mtx); tmp = src; src = buffer; buffer = tmp; } if(colorIntegrity) src.colorTransform(src.rect, _ct); if(blur) src.applyFilter(src, src.rect, src.rect.topLeft, _blurFilter); return src; } public function dispose():void { if(_baseBmd) _baseBmd.dispose(); if(_occlusionLoResBmd) _occlusionLoResBmd.dispose(); if(_bufferBmd) _bufferBmd.dispose(); _baseBmd = _occlusionLoResBmd = _bufferBmd = _lightBmp.bitmapData = null; } protected function _copyMatrix(src:Matrix, dst:Matrix):void { dst.a = src.a; dst.b = src.b; dst.c = src.c; dst.d = src.d; dst.tx = src.tx; dst.ty = src.ty; } } class VolumetricPointLight extends EffectContainer { public var _colors:Array; public var _alphas:Array; public var _ratios:Array; protected var _gradient:Shape = new Shape; protected var _gradientMtx:Matrix = new Matrix; protected var _gradientBmp:Bitmap = new Bitmap; protected var _lastSrcX:Number; protected var _lastSrcY:Number; protected var _lastIntensity:Number; protected var _lastColorIntegrity:Boolean = false; protected var _gradientLoResBmd:BitmapData; protected var _gradientLoResDirty:Boolean = true; public function get emission():DisplayObject { return _emission; } public function VolumetricPointLight(width:uint, height:uint, occlusion:DisplayObject, colorOrGradient:*, alphas:Array = null, ratios:Array = null) { if(colorOrGradient is Array) { _colors = colorOrGradient.concat(); _ratios = ratios || _colors.map(function(item:*, i:int, arr:Array):int { return 0x100*i/(colorOrGradient.length+i-1) }); _alphas = alphas || _colors.map(function(..._):Number { return 1 }); } else { _colors = [colorOrGradient, 0]; _ratios = [0, 255]; } super(width, height, _gradientBmp, occlusion); if(!occlusion) throw(new Error("An occlusion DisplayObject must be provided.")); if(!(colorOrGradient is Array || colorOrGradient is uint)) throw(new Error("colorOrGradient must be either an Array or a uint.")); } protected function _drawGradient():void { var size:Number = 2 * Math.sqrt(_viewportWidth*_viewportWidth + _viewportHeight*_viewportHeight); _gradientMtx.createGradientBox(size, size, 0, -size/2 + srcX, -size/2 + srcY); _gradient.graphics.clear(); _gradient.graphics.beginGradientFill(GradientType.RADIAL, _colors, _alphas, _ratios, _gradientMtx); _gradient.graphics.drawRect(0, 0, _viewportWidth, _viewportHeight); _gradient.graphics.endFill(); if(_gradientBmp.bitmapData) _gradientBmp.bitmapData.dispose(); _gradientBmp.bitmapData = new BitmapData(_viewportWidth, _viewportHeight, true, 0); _gradientBmp.bitmapData.draw(_gradient); } override protected function _drawLoResEmission():void { if(_gradientLoResDirty) { super._drawLoResEmission(); _gradientLoResBmd.copyPixels(_baseBmd, _baseBmd.rect, _baseBmd.rect.topLeft); _gradientLoResDirty = false; } else { _baseBmd.copyPixels(_gradientLoResBmd, _baseBmd.rect, _baseBmd.rect.topLeft); } } override protected function _updateBuffers():void { super._updateBuffers(); _gradientLoResBmd = new BitmapData(_bufferWidth, _bufferHeight, false, 0); _gradientLoResDirty = true; } override public function setViewportSize(width:uint, height:uint):void { super.setViewportSize(width, height); _drawGradient(); _gradientLoResDirty = true; } override public function render(e:Event = null):void { var srcChanged:Boolean = _lastSrcX != srcX || _lastSrcY != srcY; if(srcChanged) _drawGradient(); _gradientLoResDirty ||= srcChanged; _gradientLoResDirty ||= (!colorIntegrity && (_lastIntensity != intensity)); _gradientLoResDirty ||= (_lastColorIntegrity != colorIntegrity); _lastSrcX = srcX; _lastSrcY = srcY; _lastIntensity = intensity; _lastColorIntegrity = colorIntegrity; super.render(e); } override public function dispose():void { super.dispose(); if(_gradientLoResBmd) _gradientLoResBmd.dispose(); _gradientLoResBmd = null; } } Code Fullscreen Preview Fullscreen