package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.Sprite; import flash.display.StageDisplayState; import flash.display.TriangleCulling; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.Point; import flash.geom.Rectangle; import flash.geom.Vector3D; import flash.text.TextField; import flash.utils.getTimer; import BelousovZhabotinskyCA; [SWF(width=450, height=300, backgroundColor=0x101020, frameRate=20)] /** * ... * Petri Leskinen, 5th July 2009, in Espoo, Finland * pixelero.wordpress.com */ public class BZinPerspective extends Sprite { protected var automaton:BelousovZhabotinskyCA, canvas:BitmapData, widthScale:Number = 1.5, heightScale:Number = 1.0, rotAngle:Number = 0.0, matrix3D:Matrix3D, parallax:Boolean = true, running:Boolean = true; internal var txt:TextField, tmp:Number, i:int, testTimer:Number = 0.0, tests:int = 0; public function BZinPerspective() { automaton = new BelousovZhabotinskyCA(160, 160, 2, 6, 36); automaton.color0 = 0x103300; automaton.color1 = 0xFFFFCC; automaton.grow(12); // bitmap that's show in the player canvas = new BitmapData(stage.stageWidth/widthScale, stage.stageHeight/heightScale, true); var bitmap:Bitmap = new Bitmap(canvas); addChild(bitmap); bitmap.scaleX = widthScale; bitmap.scaleY = heightScale; matrix3D = new Matrix3D(); addEventListener(Event.ENTER_FRAME, enterFrame); stage.addEventListener(MouseEvent.CLICK, mouseClicked); stage.addEventListener(KeyboardEvent.KEY_UP, keyPressed); } private function enterFrame(e:Event = null):void { mouseMove(); if (running) update(); } private function update():void { var ix:int = 0, iy:int = 0, iz:int, pxl:uint; // do the automaton automaton.grow(); // blur for smoother appearance automaton.applyFilter(automaton, automaton.rect, new Point(), new BlurFilter(2, 2)); canvas.lock(); canvas.draw(drawPerspective(automaton, new Sprite(),canvas.width, canvas.height)); if (parallax) extrudeBitmapData(canvas, null); canvas.unlock(); } public function extrudeBitmapData(bmd:BitmapData, heightMap:BitmapData=null):void { var levels:Vector., pixels:Vector., heights:Vector., iz:int, pxl:uint, topFade:int = 6, // darkening 2^topFade pixels at top of the image alphaFades:Vector. = new Vector.(1<-1 ; ) { levels = new Vector.(bmd.height, true); pixels = bmd.getVector(column); heights = (heightMap) ? heightMap.getVector(column) : pixels ; // fade on the top for (i = -1; ++i != 1<>10) ] = i; // >>10 controls the height } // displace the column for (iz = -1, i = 0; ++i != bmd.height;) { /* if ( levels[i] > iz) { iz = levels[i]; pxl = pixels[iz]; } pixels[i] = pxl; */ pixels[i] = (levels[i] > iz) ? pxl = pixels[iz = levels[i]] : pxl ; } // rewrite on the image bmd.setVector(column, pixels); } } public function isoExtrudeBitmapData(bmd:BitmapData, heightMap:BitmapData=null):void { if (!heightMap) heightMap = bmd; var levels:Vector., pixels:Vector., heights:Vector., iz:int, pxl:uint; for (var column:Rectangle= new Rectangle(0,0,1,bmd.height); column.x < bmd.width; column.x+=1) { levels = new Vector.(bmd.height, true); heights = heightMap.getVector(column); pixels = bmd.getVector(column); for (i = 0; i != 64; i++) { pixels[i] = (pixels[i]&0xFFFFFF) | (int(i*i*256/64/64)<<24); } for (i = 0; i != bmd.height; i++) { iz = i - (int(heights[i] & 0xFF)>>1); levels[iz<0 ? 0 : iz] = i; } for (iz = -1, i = 0; i != bmd.height; i++) { /* if ( levels[i] > iz) { iz = levels[i]; pxl = pixels[iz]; } pixels[i] = pxl; */ pixels[i] = (levels[i] > iz) ? pxl = pixels[iz = levels[i]] : pxl; } bmd.setVector(column, pixels); } } private function castShadow(bmd:BitmapData):BitmapData { var tmpBmd:BitmapData = bmd.clone(); tmpBmd.draw(tmpBmd, new Matrix(1, 0, 0, 1, -1, 1), new ColorTransform(1, 1, 1, 1, tmp=-4, tmp, tmp), BlendMode.LIGHTEN); tmpBmd.draw(bmd, new Matrix(), new ColorTransform(), BlendMode.SUBTRACT); tmpBmd.draw(tmpBmd, new Matrix(), new ColorTransform(16, 16, 16)); tmpBmd.draw(tmpBmd, new Matrix(), new ColorTransform(0, 0, 0, 1, tmp=32, tmp, tmp), BlendMode.DARKEN); bmd.draw(tmpBmd, new Matrix(), new ColorTransform(), BlendMode.SUBTRACT); return bmd; } public function drawIsometric(bmd:BitmapData, s:Sprite, w:int, h:int, distantSize:Number = 5.90, nearSize:Number = 0.40, depth:Number = 5.0 ):Sprite { matrix3D.prependTranslation(5, 5, 0); txt.text = ""+matrix3D.rawData[12]; matrix3D.appendRotation(rotAngle +=0.1*(mouseX-stage.stageWidth/2), Vector3D.Z_AXIS); var uvtData:Vector. = new Vector.(); var indices:Vector. = Vector.([1,0,3, 3,2,1]); // coordinates of a rectangle w x h var vertices:Vector. = Vector.([ 0.0, 3*h/4, w/2, h, w, 3*h/4, w/2, h/2 ]); // t-values for texture mapping var distT:Number = 1.0; // / distantSize; var nearT:Number = 1.0; // / nearSize; // uvtData for the corners of the rectangle for each (var v:Vector3D in [ new Vector3D(0.0,0.0,distT), new Vector3D(0.0, 1.0,nearT), new Vector3D(1.0, 1.0,nearT), new Vector3D(1.0, 0.0,distT) ]) { v = matrix3D.transformVector(v); uvtData.push(v.x,v.y,v.z); } // update the graphics: s.graphics.clear(); s.graphics.beginBitmapFill( bmd.clone(), null, // no matrix true, // = repeat, important for 'moving' the bitmap true); // = smoothing s.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NONE); s.graphics.endFill(); return s; } public function drawPerspective(bmd:BitmapData, s:Sprite, w:int, h:int, distantSize:Number = 2.5, nearSize:Number = 0.30, depth:Number = 1.4 ):Sprite { var uvtData:Vector. = new Vector.(); // coordinates for a rectangle w x h var vertices:Vector. = Vector.([ 0.0, 0.0, 0.0, h, w, h, w, 0.0 ]); // indices of two triangles var indices:Vector. = Vector.([1, 0, 3, 3, 2, 1]); // t-values for texture mapping var distT:Number = 1.0/distantSize; var nearT:Number = 1.0/nearSize; // uvtData for the corners of the rectangle for each (var v:Vector3D in [ new Vector3D(0.5+0.5*distantSize, 1.0+depth,distT), new Vector3D(0.5+0.5*nearSize, 1.0,nearT), new Vector3D(0.5-0.5*nearSize, 1.0,nearT), new Vector3D(0.5-0.5*distantSize, 1.0+depth,distT) ]) { v = matrix3D.transformVector(v); uvtData.push(v.x,v.y,v.z); } // update the graphics: s.graphics.clear(); s.graphics.beginBitmapFill( bmd.clone(), null, // no matrix true, // = repeat, important true); // = smoothing s.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NONE); s.graphics.endFill(); return s; } // translate viewpoint by mouse's position private function mouseMove(e:MouseEvent = null):void { matrix3D.appendTranslation(0.0001*(stage.stageWidth/2 -mouseX), 0.0001*(stage.stageHeight/2 -mouseY), 0.0); } private function mouseClicked(e:MouseEvent = null):void { parallax = !parallax; } private function keyPressed(e:KeyboardEvent):void { switch (e.keyCode) { case 13: // enter =13 running = !running; break; case 32: // space =32 parallax = !parallax; update(); break; case 46: // del automaton.init(); automaton.grow(12); break; case 27: // esc stage.displayState = StageDisplayState.FULL_SCREEN; break; default: stage.displayState = StageDisplayState.NORMAL; } } } }