Forked from: makc3d's BeatPort + Metaballs 3D diff:112 Oil Slick yonatan forked:4favorite:50lines:533license : MIT License modified : 2011-05-23 15:26:34 Embed Tweet // forked from makc3d's BeatPort + Metaballs 3D // forked from makc3d's Metaballs 3D package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.LoaderInfo; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Vector3D; import flash.geom.Matrix; import flash.media.SoundMixer; import flash.net.URLRequest; import flash.system.LoaderContext; import flash.utils.ByteArray; import flash.utils.getTimer; [SWF(width=465,height=465)] /** * Ok, looks like I don't have right idea to improve this, * so maybe someone of you, oh wonderfl people, will..? * * P.S. click anywhere for next track. */ public class MetaBalls3D extends Sprite { private var texture:BitmapData; private var sphere:BauerSphere; private var metaballs:MetaBalls; private var canvas:Shape; private var skyBmd:BitmapData = new BitmapData(465, 232, false, 0); private var sea:Sea = new Sea(skyBmd); private var waves:Wave2; private var player:BeatPortPlayer; public function MetaBalls3D () { stage.quality = "medium"; BeatPortPlayer (player = new BeatPortPlayer).play (18); waves = new Wave2; waves.w1 = new Wave; waves.w2 = new Wave; //graphics.beginFill (0x202020); graphics.beginGradientFill("linear",[0x202020,0x181818,0x101010,0x080808],[1,1,1,1],[0,64,128,255],new Matrix(0.0001,0.0977,-0.1416,0.0001,80.0000,116.0000),"pad","rgb",0); graphics.drawRect (0, 0, 465, 465); addChild (sea); sea.y = 232; addChild (canvas = new Shape); canvas.x = 465 / 2; canvas.y = 465 / 2; texture = new BitmapData (67, 68, false, 0); var loader:Loader = new Loader; loader.contentLoaderInfo.addEventListener (Event.COMPLETE, gotImage); loader.load ( new URLRequest ("http://assets.wonderfl.net/images/related_images/d/dc/dc74/dc7487fc4b520bab015e52601a11101ec283167d"), new LoaderContext (true) ); sphere = new BauerSphere; sphere.debug = false; sphere.build (2000); metaballs = new MetaBalls; metaballs.a = 0; addEventListener (Event.ENTER_FRAME, draw); stage.addEventListener (MouseEvent.CLICK, nextTrack); } private function nextTrack (e:MouseEvent):void { player.next (); } private function gotImage (e:Event):void { texture.draw (LoaderInfo (e.target).content); } private var m:Matrix4 = new Matrix4; private var s:Number = 0; private var ba:ByteArray = new ByteArray; private function draw (e:Event):void { // animate waves waves.w1.P = 5e-3 * getTimer (); waves.w2.P = waves.w1.P; // update wave amplitudes based on sound var aLt:Number = 0, aLt2:Number = 0; var aRt:Number = 0, aRt2:Number = 0; if (!SoundMixer.areSoundsInaccessible ()) { var i:int; SoundMixer.computeSpectrum (ba, true); // left ba.position = 0; for (i = 0; i < 64; i++) { aLt += ba.readFloat (); } for (i = 64; i < 128; i++) { aLt2 += ba.readFloat (); } aLt /= 100; aLt2 /= 100; // right ba.position = 4 * 256; for (i = 0; i < 64; i++) { aRt += ba.readFloat (); } for (i = 64; i < 128; i++) { aRt2 += ba.readFloat (); } aRt /= 100; aRt2 /= 100; waves.w1.A = maxOrMix (waves.w1.A, 0.1 * aLt); waves.w1.A2 = maxOrMix (waves.w1.A2, 10 * aLt2); waves.w2.A = maxOrMix (waves.w2.A, 0.1 * aRt); waves.w2.A2 = maxOrMix (waves.w2.A2, 10 * aRt2); } // swing balls metaballs.a = 1 + Math.sin (2.1e-3 * getTimer ()); // rotate scene m.rotate (3.3e-4 * getTimer ()); m.Ty = -2/3; var ranges:Vector.<Range> = metaballs.ranges (); if (ranges.length > 1) { // render sphere twice if (m.Zz < 0) { morphSphere (ranges [0], waves.w1); sphere.transformVertices (m); sphere.render (canvas.graphics, texture); morphSphere (ranges [1], waves.w2); sphere.transformVertices (m); sphere.render (canvas.graphics, texture, false); } else { morphSphere (ranges [1], waves.w2); sphere.transformVertices (m); sphere.render (canvas.graphics, texture); morphSphere (ranges [0], waves.w1); sphere.transformVertices (m); sphere.render (canvas.graphics, texture, false); } } else { // render sphere once morphSphere (ranges [0], waves); sphere.transformVertices (m); sphere.render (canvas.graphics, texture); } // update sea skyBmd.fillRect(skyBmd.rect, 0); skyBmd.draw(canvas, new Matrix(1,0,0,1,232,150));//32+30)); //sea.amp = 10 + 40 * (player.channel.leftPeak + player.channel.rightPeak); sea.update(); } private function maxOrMix (a0:Number, a1:Number):Number { if (a0 < a1) return a1; return 0.9 * a0 + 0.1 * a1; } private var p:Point = new Point, q:Vector3D = new Vector3D; private function morphSphere (range:Range, wave:Wave):void { var aa:Number = (metaballs.a - 4 / 3) / 0.3; var cc:Number = 1.6 / (1 + aa * aa) - 0.8; // 0 to 0.8 to 0 when aa is -1 to 0 to +1 sphere.resetVertices (); var i:int, L:int = sphere.tvertices.length; for (i = 0; i < L; i++) { var v:Vector3D = sphere.tvertices [i]; p.x = v.x; p.y = v.y; var vz:Number = v.z; if (cc > 0) { // to get better triangulation near a = 4/3 map z to 0.2*z*|z| + 0.8*z if (vz < 0) { vz = (1 - cc) * vz - cc * vz * vz; } else { vz = (1 - cc) * vz + cc * vz * vz; } } var t:Number = 0.5 + 0.5 * vz; // shape as metaball v.z = range.x (t); p.normalize (metaballs.y (v.z)); v.x = p.x; v.y = p.y; // metaball normal p.normalize (1); q.x = p.x; q.y = p.y; q.z = -metaballs.y1 (); q.normalize (); // apply wave modifier q.scaleBy (wave.f (t)); v.incrementBy (q); } } } } class Wave { public var A:Number = 0, A2:Number = 0; public var P:Number = 0; public function f (t:Number):Number { var X:Number = 2 * t - 1; var Y:Number = ((X < 1) && (X > -1)) ? 0.5 + 0.5 * X * X : ((X > 0) ? X : -X); var S:Number = Math.sin (10 * Y - P) / Y; var C:Number = Math.cos (10000 * X); return A * S * S * (1 + A2 * C * C); } } class Wave2 extends Wave { public var w1:Wave; public var w2:Wave; override public function f (t:Number):Number { if (t < 0.5) return w1.f (2 * t); return w2.f (2 * t - 1); } } import flash.display.BitmapData; import flash.display.Graphics; import flash.display.TriangleCulling; import flash.geom.Vector3D; class Matrix4 { public var Xx:Number = 1, Yx:Number = 0, Zx:Number = 0, Tx:Number = 0; public var Xy:Number = 0, Yy:Number = 1, Zy:Number = 0, Ty:Number = 0; public var Xz:Number = 0, Yz:Number = 0, Zz:Number = 1, Tz:Number = 5; public function rotate (a:Number):void { Xx = Math.cos (a); Xz = Math.sin (a); Zx = -Math.sin (a); Zz = Math.cos (a); } } class BauerSphere { public var debug:Boolean; // original public var overtices:Vector.<Vector3D>; // transformed public var tvertices:Vector.<Vector3D>; // face normals public var faceNormals:Vector.<Vector3D>; // face map public var faceMap:Vector.<Vector.<int>>; // faces public var faceList1:Vector.<int>; public var faceList2:Vector.<int>; public var xy:Vector.<Number>; public var uv:Vector.<Number>; public function addFace (a:int, b:int, c:int):void { var L:int = faceNormals.length; faceMap [a].push (L); faceMap [b].push (L); faceMap [c].push (L); faceNormals.push (new Vector3D); faceList1.push (a, b, c); faceList2.unshift (c, a, b); } public function build (N:int = 456):void { overtices = new Vector.<Vector3D> (N, true); tvertices = new Vector.<Vector3D> (N, true); faceNormals = new <Vector3D> []; faceMap = new <Vector.<int>> []; faceList1 = new <int> []; faceList2 = new <int> []; xy = new Vector.<Number> (2 * N, true); uv = new Vector.<Number> (2 * N, true); // uniformly distributed directions: // Bauer, Robert, "Distribution of Points on a Sphere with Application to Star Catalogs", // Journal of Guidance, Control, and Dynamics, January-February 2000, vol.23 no.1 (130-137). var radius:Number = 1; for (var i:int = 1; i <= N; i++) { var phi:Number = Math.acos ( -1 + (2 * i -1) / N); var theta:Number = Math.sqrt (N * Math.PI) * phi; var rxy:Number = radius * Math.sin (phi); overtices [i - 1] = new Vector3D ( -rxy * Math.sin (theta), rxy * Math.cos (theta), radius * Math.cos (phi) ); tvertices [i - 1] = overtices [i - 1].clone (); faceMap [i - 1] = new <int> []; } // make faces (and edges), 1st and last by hand addFace (0, 1, 2); var lastEdgeA:int = 0; var lastEdgeB:int = 2; while ((lastEdgeB < N - 1) || (lastEdgeA < N - 3)) { var vA:Vector3D = tvertices [lastEdgeA]; var vB:Vector3D = tvertices [lastEdgeB]; var vA1:Vector3D = tvertices [lastEdgeA + 1]; var vB1:Vector3D; if (lastEdgeB < N - 1) vB1 = tvertices [lastEdgeB + 1]; var canIncA:Boolean = (lastEdgeA < lastEdgeB - 2) && (lastEdgeA < N - 3) && // only if B-A-A1 angle < 90° (vB.subtract (vA).dotProduct (vA1.subtract (vA)) > 0); var canIncB:Boolean = (vB1 != null) && (lastEdgeB < N - 1) && // only if B1-B-A angle < 90° (vB1.subtract (vB).dotProduct (vA.subtract (vB)) > 0); if (!(canIncA || canIncB)) break; if ( canIncA && canIncB ) { // prefer shortest edge canIncA = ( vB1.subtract (vA).lengthSquared > vA1.subtract (vB).lengthSquared ); } if (canIncA) { // add face A-B-A1 addFace (lastEdgeA, lastEdgeB, lastEdgeA + 1); // inc A lastEdgeA++; } else { // add face A-B-B1 addFace (lastEdgeA, lastEdgeB, lastEdgeB + 1); // inc B lastEdgeB++; } } // last face addFace (N - 1, N - 2, N - 3); if (debug) trace ("Sphere stats:", tvertices.length, "vertices,", faceNormals.length, "faces"); } public function resetVertices ():void { var N:int = overtices.length; for (var i:int = 0; i < N; i++) { var t:Vector3D = tvertices [i]; var o:Vector3D = overtices [i]; t.x = o.x; t.y = o.y; t.z = o.z; } } public function transformVertices (m:Matrix4):void { var N:int = overtices.length; for (var i:int = 0; i < N; i++) { var t:Vector3D = tvertices [i]; var x:Number = t.x; var y:Number = t.y; var z:Number = t.z; t.x = m.Xx * x + m.Yx * y + m.Zx * z + m.Tx; t.y = m.Xy * x + m.Yy * y + m.Zy * z + m.Ty; t.z = m.Xz * x + m.Yz * y + m.Zz * z + m.Tz; } // update face normals N = faceNormals.length; for (i = 0; i < N; i++) { var i3:int = i * 3; var a:Vector3D = tvertices [faceList1 [i3]]; var b:Vector3D = tvertices [faceList1 [i3 + 1]]; var c:Vector3D = tvertices [faceList1 [i3 + 2]]; var x1:Number = b.x - a.x, y1:Number = b.y - a.y, z1:Number = b.z - a.z; var x2:Number = c.x - a.x, y2:Number = c.y - a.y, z2:Number = c.z - a.z; t = faceNormals [i]; t.x = y1 * z2 - y2 * z1; t.y = z1 * x2 - z2 * x1; t.z = x1 * y2 - x2 * y1; } } public var normal:Vector3D = new Vector3D; public function render (gfx:Graphics, bd:BitmapData, clear:Boolean = true):void { var focalLength:Number = 400; var N:int = tvertices.length; for (var i:int = 0; i < N; i++) { var j:int = 2 * i; var t:Vector3D = tvertices [i]; // project var zoom:Number = focalLength / t.z; xy [j] = t.x * zoom; xy [j + 1] = t.y * zoom; // estimate vertex normals normal.scaleBy (0); var map:Vector.<int> = faceMap [i]; var mapL:int = map.length; for (var k:int = 0; k < mapL; k++) { normal.incrementBy (faceNormals [map [k]]); } normal.normalize (); // env. mapping uv [j] = 0.5 + 0.5 * normal.x; uv [j + 1] = 0.5 + 0.5 * normal.y; } if (clear) gfx.clear (); if (debug) gfx.lineStyle (0, 0xFF0000); gfx.beginBitmapFill (bd, null, true, true); gfx.drawTriangles (xy, // half-assed distance sorting (tvertices[1].z > tvertices[tvertices.length - 2].z) ? faceList1 : faceList2, uv, TriangleCulling.POSITIVE); } } class Range { public var a:Number; public var b:Number; public function Range (from:Number = 0, to:Number = 0) { a = from; b = to; } public function x (t:Number):Number { return a + (b - a) * t; } } /** * Metaballs calculator. * @see http://wonderfl.net/c/1TYy/read */ class MetaBalls { private var a1:Number, a2:Number, b2:Number; private var d1:Number, d2:Number; public function get a ():Number { return a1; } public function set a (A:Number):void { a1 = A; a2 = A * A; b2 = 1 + (A + a2) / 4; b2 *= b2; d1 = 3 * a2 - A - 4; d2 = 5 * a2 + A + 4; } private var x1:Number, x2:Number; private var s1:Number, s2:Number; public function y (x:Number):Number { x1 = x; x2 = x * x; s1 = Math.sqrt (Math.max (0, 4 * a2 * x2 + b2)); s2 = Math.sqrt (Math.max (0, s1 - a2 - x2)); return s2; } /** 1st derivative, dy/dx. y(x) is expected to be called 1st. */ public function y1 ():Number { return (2 * a2 * x1 / s1 - x1) / s2; } private var r1:Vector.<Range> = new <Range> [ new Range ]; private var r2:Vector.<Range> = new <Range> [ new Range, new Range ]; public function ranges ():Vector.<Range> { var S2:Number = 0.5 * Math.sqrt (d2); if (d1 > 0) { var S1:Number = 0.5 * Math.sqrt (d1); r2 [0].a = -S2; r2 [0].b = -S1; r2 [1].a = S1; r2 [1].b = S2; return r2; } r1 [0].a = -S2; r1 [0].b = S2; return r1; } } import flash.display.*; import flash.filters.*; import flash.geom.*; import flash.utils.*; class Sea extends Sprite { private const nearScale:Number = 0.1; private var vertices:Vector.<Number> = new Vector.<Number>; private var uvt:Vector.<Number> = new Vector.<Number>; private var indices:Vector.<int> = new Vector.<int>; private const octaves:int = 3; private const seed:int = 1; private var offsets:Array = []; private var output:BitmapData = new BitmapData(465, (465+1)/2, false); private var noise:BitmapData = new BitmapData(465/2, (465+3)/4, true); private var upsideDown:BitmapData = new BitmapData(465, (465+1)/2, true); private var gradient:BitmapData = new BitmapData(465, (465+1)/2, false); private var seaShape:Shape = new Shape; private var seaBmd:BitmapData = new BitmapData(465, (465+1)/2, true); private var seaBmp:Bitmap; private var ct:ColorTransform = new ColorTransform(1.5, 0.8, 0.75, 1, -128, 48, 64); private const seaMtx:Matrix = new Matrix(1, 0, 0, 1, 0, 0); private const upsideDownMtx:Matrix = new Matrix(1, 0, 0, -1, 0, 465/2); private var dispFilter:DisplacementMapFilter = new DisplacementMapFilter(seaBmd, null, 0, 8, 0, 150, "clamp"); private var backdrop:BitmapData; private var cachedOctaves:Array = []; // precalculated perlin noise public function Sea(backdrop:BitmapData) { vertices.push(0, 0, 465-1, 0, 0, 465/2-1, 465-1, 465/2-1); uvt.push( 0, 0, nearScale, 1, 0, nearScale, (1-nearScale)/2, 1, 1, (1+nearScale)/2, 1, 1); indices.push(0, 1, 2, 2, 1, 3); this.backdrop = backdrop; seaBmp = new Bitmap(seaBmd); initNoise(); addChild(new Bitmap(output)); var s:Shape = new Shape; s.graphics.beginGradientFill("linear",[0x808080,0xc0c0c0,0xe0e0e0,0xffffff],[1,1,1,1],[0,64,128,255],new Matrix(0.0001,0.0977,-0.1416,0.0001,80.0000,116.0000),"pad","rgb",0); s.graphics.drawRect (0, 0, 465, 465); gradient.draw(s); } private function initNoise():void { for(var i:int = 0; i < octaves; i++) { var bmd:BitmapData = new BitmapData(465/2, (465+3)/4, true, 0); var pr:Number = 1/Math.pow(2, i); bmd.perlinNoise(24*pr, 2*pr, 1, 0, true, true, 0xF, false); pr /= 2; bmd.colorTransform(bmd.rect, new ColorTransform(pr, pr, pr, pr)); cachedOctaves.push(bmd); } } private function fastPerlin(dst:BitmapData, xOffsets:Array):void { dst.fillRect(dst.rect, 0); for(var i:int = 0; i < octaves; i++) { var offset:int = xOffsets[i]; if(offset >= 0) { offset %= dst.width; } else { offset = dst.width - (-offset % dst.width); } var mtx:Matrix = new Matrix; mtx.tx = offset; dst.draw(cachedOctaves[i], mtx, null, "add"); mtx.tx = offset - dst.width; dst.draw(cachedOctaves[i], mtx, null, "add"); } } public function update():void { for(var i:int = 0; i < octaves; i++) offsets[i] = ((i&1)*2-1) * (1+i)*getTimer()/150; fastPerlin(noise, offsets); seaShape.graphics.clear(); seaShape.graphics.beginBitmapFill(noise, null, false, true); seaShape.graphics.drawTriangles(vertices, indices, uvt); seaShape.graphics.endFill(); seaBmd.fillRect(seaBmd.rect, 0); seaBmd.draw(seaShape, seaMtx); // reflection upsideDown.fillRect(upsideDown.rect, 0); upsideDown.draw(backdrop, new Matrix(1, 0, 0, -1, 0, 465/2), new ColorTransform(0.25,0.25,0.25)); upsideDown.draw(seaBmd, null, null, "add"); output.applyFilter(upsideDown, upsideDown.rect, upsideDown.rect.topLeft, dispFilter); output.draw(gradient, null, null, "multiply"); } } import flash.events.Event; import flash.events.IOErrorEvent; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundLoaderContext; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; class BeatPortPlayer { private var gid:int; public function next ():void { if (channel) { channel.stop (); playRandomSample (new Event ("whatever")) } } /** * Plays random samples by genre non-stop. * @see http://api.beatport.com/catalog/genres?format=xml&v=1.0 */ public function play (genreId:int):void { // 1st we need to get total number of tracks var loader:URLLoader = new URLLoader; loader.dataFormat = URLLoaderDataFormat.BINARY; subscribeLoader (loader, getCount, onIOFailure1); loader.load (new URLRequest (makeUrl (gid = genreId, 1))); } private function makeUrl (genreId:int, page:int):String { return "http://api.beatport.com/catalog/tracks?genreId=" + genreId + "&perPage=1&page=" + page + "&format=xml&v=1.0"; } private var count:int, sound:Sound, channel:SoundChannel; private var context:SoundLoaderContext = new SoundLoaderContext (10, true); private function getCount (e:Event):void { var loader:URLLoader = URLLoader (e.target); unsubscribeLoader (loader, getCount, onIOFailure1); var result:XML = XML (loader.data); count = parseInt (result.result.@count); playRandomSample (); } private function playRandomSample (e:Event = null):void { if (e != null) { unsubscribeSoundStuff (); channel = null; } var loader:URLLoader = new URLLoader; loader.dataFormat = URLLoaderDataFormat.BINARY; subscribeLoader (loader, getRandomSampleUrl, onIOFailure2); loader.load (new URLRequest (makeUrl (gid, 1 + (count - 1) * Math.random ()))); } private function getRandomSampleUrl (e:Event):void { var loader:URLLoader = URLLoader (e.target); unsubscribeLoader (loader, getRandomSampleUrl, onIOFailure2); var result:XML = XML (loader.data); channel = Sound (sound = new Sound ( new URLRequest (result.result.document.track.@url), context )).play (); subscribeSoundStuff (); } private function subscribeLoader (loader:URLLoader, onComplete:Function, onIOFailure:Function):void { loader.addEventListener (Event.COMPLETE, onComplete); loader.addEventListener (IOErrorEvent.IO_ERROR, onIOFailure); } private function unsubscribeLoader (loader:URLLoader, onComplete:Function, onIOFailure:Function):void { loader.removeEventListener (Event.COMPLETE, onComplete); loader.removeEventListener (IOErrorEvent.IO_ERROR, onIOFailure); } private function subscribeSoundStuff ():void { sound.addEventListener (IOErrorEvent.IO_ERROR, onIOFailure3); channel.addEventListener (Event.SOUND_COMPLETE, playRandomSample); } private function unsubscribeSoundStuff ():void { sound.removeEventListener (IOErrorEvent.IO_ERROR, onIOFailure3); channel.removeEventListener (Event.SOUND_COMPLETE, playRandomSample); } private function onIOFailure1 (e:IOErrorEvent):void { unsubscribeLoader (URLLoader (e.target), getCount, onIOFailure1); play (gid); } private function onIOFailure2 (e:IOErrorEvent):void { unsubscribeLoader (URLLoader (e.target), getRandomSampleUrl, onIOFailure2); playRandomSample (); } private function onIOFailure3 (e:IOErrorEvent):void { // assumes sound's ioError comes before channel's soundComplete unsubscribeSoundStuff (); playRandomSample (); } } Code Fullscreen Preview Fullscreen apocalyptic landscape: when the Earth is covered by chemical waste sea, strange life forms emerge. and dance! by makc3d at 2011/05/23 15:54:04 p.s. you should alter the texture (in photoshop, not code) to "reflect" the sea, then it's perfect! by makc3d at 2011/05/23 16:04:49 hacker_lra4m.. KameraSui bgarraud gaziya egoalcubo NINECUBE prakoczy narutohyper matacat LawrieCape sandmanjp takishiki bradsedito abakane jlbadrian dizgid Murai xor snowin kacchan6 Nyarineko poiasd brad426 tjoen linktale kuma360 soundkitchen.. djankey Thy postnum say0 paq kleinschmidt.. novita001 chamadi ProjectNya nerothos alwAYs motikawa_rgm.. Sergei.Vasil.. : Motion Graphic korooooon : 音 Kirill.Rybin.. : oil,slick,soundspectrumнефтяные капельки keim_at_Si : render zmaxlin : 3d smart_ant : 3D Bruce_Jawn : sea whirlpower : great! hidrodixtion.. : 3dawesome 9re : dubstep zob : cool! 3d awesome dubstep oil, render sea slick, sound spectrum 音 loader IOErrorEvent.IO_ERROR IOErrorEvent IOErrorEvent.COMPLETE target IOErrorEvent.SOUND_COMPLETE XML SoundLoaderContext play data URLRequest load parseInt removeEventListener vertices beginGradientFill quality Shape Loader BitmapData sort new page view favorite forked pv644 forked from: Oil Slick bradsedito forked:0 favorite:3lines:533 (diff:1) tag: 3D FlashPlayer11 Stage3D sound pv188 forked from: Oil Slick akitadn forked:0 favorite:0lines:533 (diff:1) pv238 forked from: Oil Slick andyshang forked:0 favorite:1lines:533 (diff:1) pv0 forked from: Oil Slick mitien forked:0 favorite:0lines:533 (diff:1)