/* PvColorFacets.as Lee Felarca http://www.zeropointnine.com/blog 3-2009 Source code licensed under a Creative Commons Attribution 3.0 License. http://creativecommons.org/licenses/by/3.0/ Some Rights Reserved. Import the following packages/classes to compile: - Papervision 2.0 Great White - GeodesicSphere.as and Tube.as from http://professionalpapervision.wordpress.com/2009/02/11/23-papervision3d-primitives-17-new-ones/ - Tweener And replace the Embed's below with your own.. */ package { import caurina.transitions.Tweener; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.geom.Point; import flash.utils.Dictionary; import org.papervision3d.core.geom.renderables.Triangle3D; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.math.Matrix3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.GeodesicSphere; import org.papervision3d.objects.primitives.Tube; import org.papervision3d.view.BasicView; [SWF(backgroundColor="#000000", width="640", height="480", frameRate="60")] public class PvColorFacets extends BasicView { [Embed(source="../embeds/road.jpg")] private var ClsImage1:Class; [Embed(source="../embeds/colorful.jpg")] private var ClsImage2:Class; [Embed(source="../embeds/trails.jpg")] private var ClsImage3:Class; [Embed(source="../embeds/panes.jpg")] private var ClsImage4:Class; [Embed(source="../embeds/trees.jpg")] private var ClsImage5:Class; private var _images:Array = [ ClsImage1,ClsImage2,ClsImage3,ClsImage4,ClsImage5 ]; private var _imageAt:int = -1; private var _object:DisplayObject3D; private var _filteredImage:Bitmap; private var _displayedImage:Bitmap; private var _filteredBitmap:BitmapData; private var _dicVerts2d:Dictionary; private var _rotStep:Number = 0; private var _rotStepAt:Number = 0; private var _blurFactor:int; private var _lightNormal:Number3D; public function PvColorFacets() { initOnce(); } private function initOnce():void { camera.z = -500; _filteredImage = new ClsImage1() as Bitmap; _displayedImage = new ClsImage1() as Bitmap; _filteredBitmap = new BitmapData(_filteredImage.width, _filteredImage.height); // Uncomment this to see what the filtered, source bitmap actually looks like // this.addChildAt(_filteredImage, 0); this.addChildAt(_displayedImage, 0); _lightNormal = new Number3D(-1, 1, 0.1); // up, left, and a little big behind _lightNormal.normalize(); var hit:Sprite = new Sprite(); hit.graphics.beginFill(0x00,0); hit.graphics.drawRect(0,0,640,480); hit.graphics.endFill(); hit.addEventListener(MouseEvent.CLICK, init); this.addChild(hit); this.addEventListener(Event.ENTER_FRAME, onEf); init(); } private function init($meh:*=null):void { // init objectobject if (_object) scene.removeChild(_object); _object = (Math.random() < 1.00) ? new GeodesicSphere(null, 125, 5 + int(Math.random()*5)) : new Tube(null,125,75,85, 24,12,true); scene.addChild(_object); // triangle materials for (var i:int = 0; i < _object.geometry.faces.length; i++) { Triangle3D(_object.geometry.faces[i]).material = new ColorMaterial(0x00); } // vert dictionary _dicVerts2d = new Dictionary(); for (var i:int = 0; i < _object.geometry.vertices.length; i++) { var v:Vertex3D = _object.geometry.vertices[i]; _dicVerts2d[v] = new Point(); } // do next bitmap _imageAt++; if (_imageAt > _images.length - 1) _imageAt = 0; var image:Class = _images[_imageAt]; _filteredImage.bitmapData = new image().bitmapData; _displayedImage.bitmapData = new image().bitmapData; _blurFactor = 0; // force reset meh Tweener.removeTweens(_object); moveObject(); } private function moveObject():void { // randomize translation Tweener.addTween(_object, { x:Math.random()*400-200, y:Math.random()*300-150, z:Math.random()*400-200, time:2 + Math.random()*2, transition:"easeInOutSine", onComplete:moveObject } ); // randomize rotation _rotStep = Math.random() * 1.75; _rotStep *= _rotStep; // range [0,3] } private function onEf($e:Event):void { update(); renderer.renderScene(scene,camera,viewport); } private function update():void { // rotate sphere _rotStepAt += (_rotStep - _rotStepAt) / 10; _object.rotationY += _rotStepAt; _object.rotationX += _rotStepAt * 0.33; // rotate light source for fun _lightNormal.rotateY(_rotStepAt * -0.2); _lightNormal.rotateX(_rotStepAt * 0.33 * -0.2); // put screen pos of object's vertices into dictionary var v:Vertex3D; for (var i:int = 0; i < _object.geometry.vertices.length; i++) { v = _object.geometry.vertices[i]; _dicVerts2d[v].x = v.vertex3DInstance.x + viewport.viewportWidth/2; _dicVerts2d[v].y = v.vertex3DInstance.y + viewport.viewportHeight/2; } var normalizedZ:Number = 1 - (_object.z + 200) / 400; // range [0,1] var t:Triangle3D; var centerX:Number, centerY:Number; var colV1:uint, colV2:uint, colV3:uint, colCenter:uint, col:uint; var n:Number3D = new Number3D(); var lightFactor:Number; for (var j:int = 0; j < _object.geometry.faces.length; j++) { t = _object.geometry.faces[j]; // [1] Get bitmapData colors under triangle colV1 = _filteredBitmap.getPixel(_dicVerts2d[t.v0].x,_dicVerts2d[t.v0].y); colV2 = _filteredBitmap.getPixel(_dicVerts2d[t.v1].x,_dicVerts2d[t.v1].y); colV3 = _filteredBitmap.getPixel(_dicVerts2d[t.v2].x,_dicVerts2d[t.v2].y); centerX = (_dicVerts2d[t.v0].x + _dicVerts2d[t.v1].x + _dicVerts2d[t.v2].x) / 3; centerY = (_dicVerts2d[t.v0].y + _dicVerts2d[t.v1].y + _dicVerts2d[t.v2].y) / 3; colCenter = _filteredBitmap.getPixel(centerX, centerY); col = averageColors(colV1,colV2,colV3,colCenter); // [2] Rolling own FlatShader effect n.x = t.faceNormal.x; n.y = t.faceNormal.y; n.z = t.faceNormal.z; Matrix3D.multiplyVector3x3(_object.world, n); lightFactor = Number3D.dot(n,_lightNormal); // range [-1,1] lightFactor *= 0.15 + ((1-normalizedZ) * 0.70); // range [0.85,0.15] col = adjustBrightness2(col, lightFactor*100); t.material.fillColor = col; } // Blur filter softens color differences between adjacent pixels var blr:int = 6 + (normalizedZ * 20); blr = int(blr/2)*2; if (blr != _blurFactor) { // (avoid updating blur filter too frequently) _blurFactor = blr; _filteredImage.filters = [ new BlurFilter(_blurFactor, _blurFactor, 2) ]; _filteredBitmap.draw(_filteredImage); } } // ----------------------------------------------------------------------------- // HELPER COLOR FUNCTIONS // ----------------------------------------------------------------------------- private static function averageColors(...$rest):uint { var r:int, g:int, b:int; for (var i:int = 0; i < $rest.length; i++) { r += $rest[i] >> 16; g += $rest[i] >> 8 & 0xff; b += $rest[i] & 0xff; } r = int( Math.round(r/$rest.length)); g = int( Math.round(g/$rest.length)); b = int( Math.round(b/$rest.length)); return r << 16 | g << 8 | b; } private static function lerpColor($val:Number, $colMin:uint, $colMax:uint):uint { var rmin:int = $colMin >> 16; var rmax:int = $colMax >> 16; var rnew:int = lerpInteger($val, rmin, rmax); var gmin:int = $colMin >> 8 & 0xff; var gmax:int = $colMax >> 8 & 0xff; var gnew:int = lerpInteger($val, gmin, gmax); var bmin:int = $colMin & 0xff; var bmax:int = $colMax & 0xff; var bnew:int = lerpInteger($val, bmin, bmax); return rnew << 16 | gnew << 8 | bnew; } public static function lerpInteger(normValue:Number, minimum:int, maximum:int):int { return int(Math.round(minimum + (maximum - minimum) * normValue)); } /** * mx.utils.ColorUtil * @param brite Range -100 to 100 */ public static function adjustBrightness2(rgb:uint, brite:Number):uint { var r:Number; var g:Number; var b:Number; if (brite == 0) return rgb; if (brite < 0) { brite = (100 + brite) / 100; r = ((rgb >> 16) & 0xFF) * brite; g = ((rgb >> 8) & 0xFF) * brite; b = (rgb & 0xFF) * brite; } else // bright > 0 { brite /= 100; r = ((rgb >> 16) & 0xFF); g = ((rgb >> 8) & 0xFF); b = (rgb & 0xFF); r += ((0xFF - r) * brite); g += ((0xFF - g) * brite); b += ((0xFF - b) * brite); r = Math.min(r, 255); g = Math.min(g, 255); b = Math.min(b, 255); } return (r << 16) | (g << 8) | b; } } }