Away3D [plane/fluid/draggable]
////////////////////////////////////////////////////////////////////////////////
// Away3D [plane/fluid/draggable]
//
// [Away3D] 平面 (4)
// http://www.project-nya.jp/modules/weblog/details.php?blog_id=1773
//
// pixel bender のpbjファイルをbase64エンコードしてAS3にするツール
// http://wonderfl.net/c/wmEK
////////////////////////////////////////////////////////////////////////////////
/**
* Copyright ProjectNya ( http://wonderfl.net/user/ProjectNya )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/CcTp
*/
////////////////////////////////////////////////////////////////////////////////
// Away3D [plane/fluid/draggable]
//
// [Away3D] 平面 (4)
// http://www.project-nya.jp/modules/weblog/details.php?blog_id=1773
//
// pixel bender のpbjファイルをbase64エンコードしてAS3にするツール
// http://wonderfl.net/c/wmEK
////////////////////////////////////////////////////////////////////////////////
package {
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.system.System;
import flash.events.Event;
import flash.geom.Vector3D;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.events.MouseEvent;
import flash.geom.Point;
import away3d.Away3D;
import away3d.containers.View3D;
import away3d.containers.Scene3D;
import away3d.cameras.Camera3D;
import away3d.lights.PointLight;
import away3d.entities.Mesh;
import away3d.primitives.SkyBox;
import away3d.primitives.PlaneGeometry;
import away3d.materials.ColorMaterial;
import away3d.textures.BitmapCubeTexture;
import away3d.materials.lightpickers.StaticLightPicker;
import away3d.materials.methods.EnvMapMethod;
import away3d.events.MouseEvent3D;
import away3d.core.pick.PickingColliderType;
import away3d.debug.AwayStats;
[SWF(backgroundColor="#000000", width="600", height="600", frameRate="60")]
public class Main extends Sprite {
private var loaders:Array;
private var data:Array;
private var loaded:uint = 0;
private static var basePath:String = "http://www.project-nya.jp/images/wonderfl/environment/";
private var view:View3D;
private var scene:Scene3D;
private var camera:Camera3D;
private var light:PointLight;
private var water:Mesh;
private var fluid:ShallowFluid;
private var disturb:FluidDisturb;
private var brush:DisturbanceBrush;
private static var radius:uint = 500;
private var angle:Number = 90;
private static var radian:Number = Math.PI/180;
private static var center:Vector3D = new Vector3D();
private var point:Point;
private var pressing:Boolean = false;
private var stats:AwayStats;
//private var source:BitmapData = new BitmapData(465, 465, false, 0x000000);
public function Main() {
Wonderfl.disable_capture();
//addChild(new Bitmap(source));
//
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
System.pauseForGCIfCollectionImminent(1);
init();
stats = new AwayStats(view);
addChild(stats);
stats.x = 475;
}
private function init():void {
view = new View3D();
scene = view.scene;
camera = view.camera;
addChild(view);
light = new PointLight();
scene.addChild(light);
setup();
//
var imagePaths:Array = new Array();
imagePaths.push("right.jpg");
imagePaths.push("left.jpg");
imagePaths.push("top.jpg");
imagePaths.push("bottom.jpg");
imagePaths.push("front.jpg");
imagePaths.push("back.jpg");
loaders = new Array();
data = new Array();
for (var n:uint = 0; n < 6; n++) {
var loader:ImageLoader = new ImageLoader();
loader.id = n;
loader.addEventListener(ImageLoader.COMPLETE, complete, false, 0, true);
loader.load(basePath + imagePaths[n], true);
loaders.push(loader);
}
}
private function complete(evt:Event):void {
var loader:ImageLoader = ImageLoader(evt.target);
loader.removeEventListener(ImageLoader.COMPLETE, complete);
data[loader.id] = loader.content.bitmapData;
loader = null;
//
loaded ++;
if (loaded > 5) {
initialize();
addEventListener(Event.ENTER_FRAME, render, false, 0, true);
}
}
private function setup():void {
view.backgroundColor = 0x000000;
view.antiAlias = 4;
//
camera.x = 0;
camera.y = 200;
camera.z = - radius;
//
light.x = 0;
light.y = 0;
light.z = 0;
light.specular = 0.5;
light.diffuse = 2;
light.ambient = 0;
}
private function initialize():void {
var map:BitmapCubeTexture = new BitmapCubeTexture(data[0], data[1], data[2], data[3], data[4], data[5]);
var sky:SkyBox = new SkyBox(map);
scene.addChild(sky);
//
var geometry:PlaneGeometry = new PlaneGeometry(400, 400, 200, 200, true, false);
var material:ColorMaterial = new ColorMaterial(0xFFFFFF, 1);
material.specular = 0.5;
material.ambient = 0.25;
material.ambientColor = 0x111199;
material.ambient = 1;
material.addMethod(new EnvMapMethod(map, 1));
var lightPicker:StaticLightPicker = new StaticLightPicker([light]);
material.lightPicker = lightPicker;
//
water = new Mesh(geometry, material);
water.rotationX = 90;
water.x = - 200;
water.y = 0;
water.z = - 200;
scene.addChild(water);
water.mouseEnabled = true;
water.pickingCollider = PickingColliderType.BOUNDS_ONLY;
water.geometry.subGeometries[0].autoDeriveVertexNormals = false;
water.geometry.subGeometries[0].autoDeriveVertexTangents = false;
//
var dt:Number = 1/stage.frameRate;
fluid = new ShallowFluid(201, 201, 2, dt, 0.99, 0.3);
disturb = new FluidDisturb(fluid);
brush = new DisturbanceBrush();
brush.fromSprite(new Brush());
//
water.addEventListener(MouseEvent3D.MOUSE_DOWN, click);
water.addEventListener(MouseEvent3D.MOUSE_MOVE, mouseMove);
view.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
view.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
}
private function render(evt:Event):void {
fluid.evaluate();
disturb.updateMemoryDisturbances();
water.geometry.subGeometries[0].updateVertexData(fluid.points);
water.geometry.subGeometries[0].updateVertexNormalData(fluid.normals);
water.geometry.subGeometries[0].updateVertexTangentData(fluid.tangents);
//disturb.disturbBitmapInstant(0.5, 0.5, -5, brush.bitmapData);
if (pressing) {
disturb.disturbBitmapInstant(point.x, point.y, - 5, brush.bitmapData);
}
//
angle += 0.5;
camera.x = radius*Math.cos(angle*radian);
camera.y = 200;
camera.z = radius*Math.sin(angle*radian);
camera.lookAt(center);
view.render();
//view.renderer.queueSnapshot(source);
}
private function wave(x:Number, y:Number):void {
point = new Point(x/400, y/400);
}
private function click(evt:MouseEvent3D):void {
pressing = true;
wave(evt.localPosition.x, evt.localPosition.y);
}
private function mouseMove(evt:MouseEvent3D):void {
if (pressing) {
wave(evt.localPosition.x, evt.localPosition.y);
}
}
private function mouseDown(evt:MouseEvent):void {
stage.addEventListener(Event.MOUSE_LEAVE, leave, false, 0, true);
}
private function mouseUp(evt:MouseEvent):void {
pressing = false;
stage.removeEventListener(Event.MOUSE_LEAVE, leave);
}
private function leave(evt:Event):void {
pressing = false;
stage.removeEventListener(Event.MOUSE_LEAVE, leave);
}
}
}
//////////////////////////////////////////////////
// ShallowFluidクラス (com.li.fluids.shallow.ShallowFluid)
//////////////////////////////////////////////////
// Calculates displacement, normals and tangents for a fluid grid simulation
// using shallow water wave equations.
// Uses pixel bender shaders via number vectors.
import flash.display.Shader;
import flash.display.ShaderJob;
import flash.utils.ByteArray;
class ShallowFluid {
private var _width:uint;
private var _height:uint;
private var _spacing:Number;
private var _k1:Number, _k2:Number, _k3:Number;
private var _points:Vector.<Vector.<Number>>;
private var _renderBuffer:uint = 1;
private var _normals:Vector.<Number>;
private var _tangents:Vector.<Number>;
private var _dt:Number;
private var _realWaveSpeed:Number;
private var _wantedWaveSpeed:Number;
private var _viscosity:Number;
private var _displacementShader:Shader;
private var _normalsShader:Shader;
private var _tangentsShader:Shader;
public function ShallowFluid( n:uint, m:uint, d:Number, t:Number, c:Number, mu:Number ) {
_width = n;
_height = m;
_spacing = d;
_dt = t;
_viscosity = mu;
// Init buffers.
_points = new Vector.<Vector.<Number>>();
_points[0] = new Vector.<Number>();
_points[1] = new Vector.<Number>();
_normals = new Vector.<Number>();
_tangents = new Vector.<Number>();
// Fill buffers.
var a:uint, j:uint, i:uint;
for( j = 0; j < m; j++ ) {
var y:Number = d * j;
for( i = 0; i < n; i++ ) {
_points[0].push( d * i, y, 0.0 );
_points[1].push( d * i, y, 0.0 );
_normals.push( 0.0, 0.0, 2.0 * d );
_tangents.push( 2.0 * d, 0.0, 0.0 );
a++;
}
}
// Initialize normals shader.
//_normalsShader = new Shader( new NormalsShader() as ByteArray );
var normalsShaderLoader:NormalsShader = new NormalsShader();
_normalsShader = normalsShaderLoader.load();
_normalsShader.data.dd.value = [-2.0 * d];
// Initialize normals shader.
//_tangentsShader = new Shader( new TangentsShader() as ByteArray );
var tangentsShaderLoader:TangentsShader = new TangentsShader();
_tangentsShader = tangentsShaderLoader.load();
_tangentsShader.data.dd.value = [-2.0 * d];
// Initialize displacement shader.
//_displacementShader = new Shader( new DisplacementShader() as ByteArray );
var displacementShaderLoader:DisplacementShader = new DisplacementShader();
_displacementShader = displacementShaderLoader.load();
switchBuffers();
// Evaluate wave speed number and init constants.
speed = c;
}
// Performa a calculation cycle.
public function evaluate():void {
// Evaluate displacement.
var displacementJob:ShaderJob = new ShaderJob( _displacementShader, _points[1 - _renderBuffer], _width, _height );
displacementJob.start( true );
// Evaluate normals.
var normalsJob:ShaderJob = new ShaderJob( _normalsShader, _normals, _width, _height );
normalsJob.start( true );
// Evaluate tangents.
var tangentsJob:ShaderJob = new ShaderJob( _tangentsShader, _tangents, _width, _height );
tangentsJob.start( true );
switchBuffers();
}
// Displaces a point in the current and previous buffer to a given position.
public function displacePointStatic( n:uint, m:uint, displacement:Number ):void {
var index:int = _width * m + n;
_points[_renderBuffer][3 * index + 2] = displacement;
_points[1 - _renderBuffer][3 * index + 2] = displacement;
}
// Displaces a point in the current and previous buffer by a given amount.
public function displacePoint( n:uint, m:uint, displacement:Number ):void {
var index:int = _width * m + n;
_points[_renderBuffer][3 * index + 2] += displacement;
_points[1 - _renderBuffer][3 * index + 2] += displacement;
}
// WaveSpeed. Changes the speed of the simulation, with other collateral effects. Input between >0 and <1.
public function set speed( value:Number ):void {
_wantedWaveSpeed = value;
_realWaveSpeed = value * (_spacing / (2 * _dt)) * Math.sqrt( _viscosity * _dt + 2 );
preCalculateConstants();
}
public function get speed():Number {
return _realWaveSpeed;
}
// Viscosity.
public function get viscosity():Number {
return _viscosity;
}
public function set viscosity( value:Number ):void {
_viscosity = value;
speed = _wantedWaveSpeed;
preCalculateConstants();
}
// Get fluid normals.
public function get normals():Vector.<Number> {
return _normals;
}
// Get fluid tangents.
public function get tangents():Vector.<Number> {
return _tangents;
}
// Get fluid points.
public function get points():Vector.<Number> {
return _points[_renderBuffer];
}
// Get fluid dimensions.
public function get gridWidth():Number {
return _width;
}
public function get gridHeight():Number {
return _height;
}
public function get gridSpacing():Number {
return _spacing;
}
private function preCalculateConstants():void {
var f1:Number = _realWaveSpeed * _realWaveSpeed * _dt * _dt / (_spacing * _spacing);
var f2:Number = 1 / (_viscosity * _dt + 2);
_k1 = (4 - 8 * f1) * f2;
_k2 = (_viscosity * _dt - 2) * f2;
_k3 = 2 * f1 * f2;
_displacementShader.data.k1.value = [_k1];
_displacementShader.data.k2.value = [_k2];
_displacementShader.data.k3.value = [_k3];
_displacementShader.data.dims.value = [_width - 1, _height - 1];
}
private function switchBuffers():void {
_renderBuffer = 1 - _renderBuffer;
_displacementShader.data.currentBuffer.input = _points[_renderBuffer];
_displacementShader.data.previousBuffer.input = _points[1 - _renderBuffer];
_displacementShader.data.currentBuffer.width = _width;
_displacementShader.data.currentBuffer.height = _height;
_displacementShader.data.previousBuffer.width = _width;
_displacementShader.data.previousBuffer.height = _height;
_normalsShader.data.currentBuffer.input = _points[_renderBuffer];
_normalsShader.data.currentBuffer.width = _width;
_normalsShader.data.currentBuffer.height = _height;
_tangentsShader.data.currentBuffer.input = _points[_renderBuffer];
_tangentsShader.data.currentBuffer.width = _width;
_tangentsShader.data.currentBuffer.height = _height;
}
}
//////////////////////////////////////////////////
// NormalsShaderクラス (WaveNormals.pbj)
//////////////////////////////////////////////////
import flash.display.Shader;
import mx.utils.Base64Decoder;
class NormalsShader {
private var decoder:Base64Decoder;
public function NormalsShader() {
init();
}
private function init():void {
decoder = new Base64Decoder();
decoder.decode("pQEAAACkCwBXYXZlTm9ybWFsc6AMbmFtZXNwYWNlAACgDHZlbmRvcgBMaQCgCHZlcnNpb24AAQCgDGRlc2NyaXB0aW9uAENhbGN1bGF0ZXMgbm9ybWFsIHZhbHVlcyBmb3IgYSAyRCB3YXZlIGVxdWF0aW9uLgChAQIAAAxfT3V0Q29vcmQAowADY3VycmVudEJ1ZmZlcgChAgMBAA5kc3QAoQEBAAACZGQAogFkZWZhdWx0VmFsdWUAAAAAAB0CAMEAABAAMgIAID+AAAAyAgAQAAAAAB0DAMECABAAAQMAwQIAsAAwBADhAwAQAB0DAOIEABgAMgIAIL+AAAAyAgAQAAAAAB0EAMECABAAAQQAwQIAsAAwBQDhBAAQAB0EAOIFABgAMgIAIAAAAAAyAgAQv4AAAB0FAMECABAAAQUAwQIAsAAwBgDhBQAQAB0FAOIGABgAMgIAIAAAAAAyAgAQP4AAAB0GAMECABAAAQYAwQIAsAAwBwDhBgAQAB0GAOIHABgAHQAAEAMAgAACAAAQBACAAB0BAIAAAMAAHQAAEAYAgAACAAAQBQCAAB0BAEAAAMAAHQEAIAAAgAA=");
}
public function load():Shader {
return new Shader(decoder.toByteArray());
}
}
//////////////////////////////////////////////////
// TangentsShaderクラス (WaveTangents.pbj)
//////////////////////////////////////////////////
import flash.display.Shader;
import mx.utils.Base64Decoder;
class TangentsShader {
private var decoder:Base64Decoder;
public function TangentsShader() {
init();
}
private function init():void {
decoder = new Base64Decoder();
decoder.decode("pQEAAACkDABXYXZlVGFuZ2VudHOgDG5hbWVzcGFjZQAAoAx2ZW5kb3IATGkAoAh2ZXJzaW9uAAEAoAxkZXNjcmlwdGlvbgBDYWxjdWxhdGVzIHRhbmdlbnQgdmFsdWVzIGZvciBhIDJEIHdhdmUgZXF1YXRpb24uAKEBAgAADF9PdXRDb29yZACjAANjdXJyZW50QnVmZmVyAKECAwEADmRzdAChAQEAAAJkZACiAWRlZmF1bHRWYWx1ZQAAAAAAHQIAwQAAEAAyAgAgP4AAADICABAAAAAAHQMAwQIAEAABAwDBAgCwADAEAOEDABAAHQMA4gQAGAAyAgAgv4AAADICABAAAAAAHQQAwQIAEAABBADBAgCwADAFAOEEABAAHQQA4gUAGAAdAQCAAACAADIBAEAAAAAAHQAAEAQAgAACAAAQAwCAAB0BACAAAMAA");
}
public function load():Shader {
return new Shader(decoder.toByteArray());
}
}
//////////////////////////////////////////////////
// DisplacementShaderクラス (WaveDisplacement.pbj)
//////////////////////////////////////////////////
import flash.display.Shader;
import mx.utils.Base64Decoder;
class DisplacementShader {
private var decoder:Base64Decoder;
public function DisplacementShader() {
init();
}
private function init():void {
decoder = new Base64Decoder();
decoder.decode("pQEAAACkEABXYXZlRGlzcGxhY2VtZW50oAxuYW1lc3BhY2UAAKAMdmVuZG9yAExpAKAIdmVyc2lvbgABAKAMZGVzY3JpcHRpb24AQ2FsY3VsYXRlcyBkaXNwbGFjZW1lbnQgdmFsdWVzIGZvciBhIDJEIHdhdmUgZXF1YXRpb24uAKEBAgAADF9PdXRDb29yZACjAANjdXJyZW50QnVmZmVyAKMBA3ByZXZpb3VzQnVmZmVyAKECAwEADmRzdAChAQEAAAJrMQCiAWRlZmF1bHRWYWx1ZQAAAAAAoQEBAAABazIAogFkZWZhdWx0VmFsdWUAAAAAAKEBAQEAAWszAKIBZGVmYXVsdFZhbHVlAAAAAAChAQICAAxkaW1zAKICZGVmYXVsdFZhbHVlAAAAAAAAAAAAHQIAMQAAEAAwAwDhAgCwAB0EAOIDABgAMgMAgD+AAAAqAwCAAgCAAB0BgIAAgAAAMgMAgD+AAAAqAwCAAgDAAB0BgEAAgAAAHQGAIAGAAAAtAYAgAYBAACoCACACAAAAHQGAgACAAAAdAYBAAYCAAC0BgEABgAAAKgIAEAIAQAAdAYCAAIAAAB0BgCABgEAALQGAIAGAAAA0AAAAAYCAADADAOECALABHQUA4gMAGAAyAwCAP4AAADIDAEAAAAAAHQMAMQIAsAABAwAxAwAQADAGAOEDALAAHQMA4gYAGAAyBgCAv4AAADIGAEAAAAAAHQYAMQIAsAABBgAxBgAQADAHAOEGALAAHQYA4gcAGAAyBwCAAAAAADIHAEC/gAAAHQcAMQIAsAABBwAxBwAQADAIAOEHALAAHQcA4ggAGAAyCACAAAAAADIIAEA/gAAAHQgAMQIAsAABCAAxCAAQADAJAOEIALAAHQgA4gkAGAAdAwAQAACAAAMDABAEAIAAHQQAEAAAwAADBAAQBQCAAB0FABADAMAAAQUAEAQAwAAdAwAQAwCAAAEDABAGAIAAHQQAEAMAwAABBAAQBwCAAB0DABAEAMAAAQMAEAgAgAAdBAAQAQDAAAMEABADAMAAHQMAEAUAwAABAwAQBADAAB0BACADAMAANQAAAAAAAAAyAQAgAAAAADYAAAAAAAAAHQEAgAQAAAAdAQBABABAAA==");
}
public function load():Shader {
return new Shader(decoder.toByteArray());
}
}
//////////////////////////////////////////////////
// FluidDisturbクラス (com.li.fluids.shallow.FluidDisturb)
//////////////////////////////////////////////////
// Utility class that produces natural disturbances in a fluid simulation.
import flash.display.BitmapData;
import flash.geom.Vector3D;
class FluidDisturb {
private var _fluid:ShallowFluid;
private var _memoryDisturbances:Vector.<MemoryDisturbance>;
public function FluidDisturb( fluid:ShallowFluid ) {
_fluid = fluid;
_memoryDisturbances = new Vector.<MemoryDisturbance>();
}
// Disturbs the fluid using a bitmap image.
public function disturbBitmapInstant( x:Number, y:Number, displacement:Number, image:BitmapData ):void {
var i:uint, j:uint;
var ix:Number, iy:Number;
var gray:uint;
// Precalculations.
var imageGridWidth:uint = Math.floor( image.width / _fluid.gridSpacing );
var imageGridHeight:uint = Math.floor( image.height / _fluid.gridSpacing );
var sx:uint = Math.floor( _fluid.gridWidth * x ) - Math.floor( imageGridWidth / 2 );
var sy:uint = Math.floor( _fluid.gridHeight * y ) - Math.floor( imageGridHeight / 2 );
var ex:uint = sx + imageGridWidth;
var ey:uint = sy + imageGridHeight;
// Avoid over flows.
if( sx < 0 || sy < 0 || ex > _fluid.gridWidth || ey > _fluid.gridHeight )
return;
// Loop.
for( i = sx; i < ex; i++ ) {
for( j = sy; j < ey; j++ ) {
ix = Math.floor( image.width * (i - sx) / imageGridWidth );
iy = Math.floor( image.height * (j - sy) / imageGridHeight );
gray = image.getPixel( ix, image.height - iy ) & 0x0000FF;
if( gray != 0 )
_fluid.displacePoint( i, j, displacement * gray / 256 );
}
}
}
// Disturbs the fluid using a bitmap image. The disturbance remains for a given time.
public function disturbBitmapMemory( x:Number, y:Number, displacement:Number, image:BitmapData, time:int, speed:Number ):void {
var disturbance:MemoryDisturbance = new MemoryDisturbance( time, speed );
_memoryDisturbances.push( disturbance );
var i:uint, j:uint;
var ix:Number, iy:Number;
var gray:uint;
// Precalculations.
var imageGridWidth:uint = Math.floor( image.width / _fluid.gridSpacing );
var imageGridHeight:uint = Math.floor( image.height / _fluid.gridSpacing );
var sx:uint = Math.floor( _fluid.gridWidth * x ) - Math.floor( imageGridWidth / 2 );
var sy:uint = Math.floor( _fluid.gridHeight * y ) - Math.floor( imageGridHeight / 2 );
var ex:uint = sx + imageGridWidth;
var ey:uint = sy + imageGridHeight;
// Avoid over flows.
if( sx < 0 || sy < 0 || ex > _fluid.gridWidth || ey > _fluid.gridHeight )
return;
// Loop.
for( i = sx; i < ex; i++ ) {
for( j = sy; j < ey; j++ ) {
ix = Math.floor( image.width * (i - sx) / imageGridWidth );
iy = Math.floor( image.height * (j - sy) / imageGridHeight );
gray = image.getPixel( ix, image.height - iy ) & 0x0000FF;
if( gray != 0 )
disturbance.addDisturbance( i, j, displacement * gray / 256 );
}
}
}
// Disturb a point with no smoothing. Fast, but unnatural.
public function disturbPoint( n:Number, m:Number, displacement:Number ):void {
_fluid.displacePoint( Math.floor( n * _fluid.gridWidth ), Math.floor( m * _fluid.gridHeight ), displacement );
}
// Produces a circular, gaussian bell shaped disturbance in the fluid.
// Results in natural, jaggedless, drop-like disturbances.
// n - [0, 1] - x coordinate.
// m - [0, 1] - y coordinate.
// displacement - z displacement of the disturbance.
// radius - controls the opening of the gaussian bell and the wideness of the affected sub-grid.
public function disturbPointGaussian( n:Number, m:Number, displacement:Number, radius:Number ):void {
// Id target point in grid.
var epiX:uint = Math.floor( n * _fluid.gridWidth );
var epiY:uint = Math.floor( m * _fluid.gridHeight );
// Find start point.
var sX:uint = epiX - radius / 2;
var sY:uint = epiY - radius / 2;
// Loop.
var i:uint, j:uint;
var x:uint, y:uint, d:Number, dd:Number, dx:Number, dy:Number;
var maxDis:Number = radius / 2;
for( i = 0; i < radius; i++ ) {
for( j = 0; j < radius; j++ ) {
x = sX + i;
y = sY + j;
if( x == epiX && y == epiY ) {
_fluid.displacePoint( x, y, displacement );
} else {
// Eval distance to epicenter.
dx = epiX - x;
dy = epiY - y;
dd = dx * dx + dy * dy;
d = Math.sqrt( dd );
if( d < maxDis ) {
_fluid.displacePoint( x, y, displacement * Math.pow( 2, -dd * radius / 100 ) ); // Gaussian distribution (could have many options here).
}
}
}
}
}
public function releaseMemoryDisturbances():void {
var i:uint;
var loop:uint = _memoryDisturbances.length;
for( i = 0; i < loop; i++ ) {
var memoryDisturbance:MemoryDisturbance = _memoryDisturbances[i];
memoryDisturbance.concluded = true;
}
}
public function updateMemoryDisturbances():void {
var i:uint, j:uint;
var loop:uint = _memoryDisturbances.length;
for( i = 0; i < loop; i++ ) {
var memoryDisturbance:MemoryDisturbance = _memoryDisturbances[i];
// Advance the memory disturbance's time.
memoryDisturbance.update();
// Check caducity.
if( memoryDisturbance.concluded ) {
memoryDisturbance = null;
_memoryDisturbances.splice( i, 1 );
i--;
loop--;
continue;
}
// Update the memory disturbance's points on the fluid.
var subLoop:uint = memoryDisturbance.disturbances.length;
for( j = 0; j < subLoop; j++ ) {
var disturbance:Vector3D = memoryDisturbance.disturbances[j];
_fluid.displacePointStatic( disturbance.x, disturbance.y, disturbance.z * memoryDisturbance.growth );
}
}
}
}
//////////////////////////////////////////////////
// MemoryDisturbanceクラス (com.li.fluids.shallow.MemoryDisturbance)
//////////////////////////////////////////////////
// time is the time that the disturbance will last. if -1, disturbance lasts until manually concluded.
import flash.geom.Vector3D;
import flash.utils.getTimer;
class MemoryDisturbance {
private var _disturbances:Vector.<Vector3D>;
private var _targetTime:int;
private var _elapsedTime:uint;
private var _startTime:uint;
private var _concluded:Boolean;
private var _growthRate:Number;
private var _growth:Number;
public function MemoryDisturbance( time:int, speed:Number ) {
_targetTime = time;
_startTime = getTimer();
_disturbances = new Vector.<Vector3D>();
_growth = 0;
_growthRate = speed;
}
public function get growth():Number {
return _growth;
}
public function get disturbances():Vector.<Vector3D> {
return _disturbances;
}
public function addDisturbance( x:uint, y:uint, displacement:Number ):void {
_disturbances.push( new Vector3D( x, y, displacement ) );
}
public function update():void {
if( _concluded )
return;
_growth += _growthRate;
_growth = _growth > 1 ? 1 : _growth;
if( _targetTime < 0 )
return;
_elapsedTime = getTimer() - _startTime;
if( _elapsedTime >= _targetTime )
_concluded = true;
}
public function get concluded():Boolean {
return _concluded;
}
public function set concluded( value:Boolean ):void {
_concluded = value;
}
}
//////////////////////////////////////////////////
// DisturbanceBrushクラス (com.li.fluids.shallow.DisturbanceBrush)
//////////////////////////////////////////////////
// Handles bitmap images to use with a FluidDisturb on a Fluid simulation.
import flash.display.BitmapData;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.Sprite;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
import flash.geom.Rectangle;
class DisturbanceBrush {
private var _bmd:BitmapData;
public function DisturbanceBrush() {
}
public function generateGradient( radius:Number ):void {
var drawer:Sprite = new Sprite();
var fillType:String = GradientType.RADIAL;
var colors:Array = [0xFFFFFF, 0x000000];
var alphas:Array = [1, 1];
var ratios:Array = [0x00, 0xFF];
var matrix:Matrix = new Matrix();
matrix.createGradientBox( radius, radius, 0, 0, 0 );
var spreadMethod:String = SpreadMethod.PAD;
drawer.graphics.beginGradientFill( fillType, colors, alphas, ratios, matrix, spreadMethod );
drawer.graphics.drawRect( 0, 0, radius, radius );
drawer.graphics.endFill();
fromSprite( drawer );
}
// Converts a sprite to a bitmapData with blur.
// Adds bleeding to avoid liquid stickiness to image's edges.
// Accounts for bounding box variations due to blur.
public function fromSprite( spr:Sprite, blur:Number = 4 ):void {
_bmd = new BitmapData( spr.width, spr.height, false, 0x000000 );
var blurFilter:BlurFilter = new BlurFilter( blur, blur, 3 );
var blurRect:Rectangle = _bmd.generateFilterRect( _bmd.rect, blurFilter );
_bmd = new BitmapData( blurRect.width, blurRect.height, false, 0x000000 );
var matrix:Matrix = new Matrix();
matrix.translate( -blurRect.x, -blurRect.y );
spr.filters = [blurFilter];
_bmd.draw( spr, matrix );
}
public function set bitmapData( value:BitmapData ):void {
_bmd = value;
}
public function get bitmapData():BitmapData {
return _bmd;
}
}
//////////////////////////////////////////////////
// ImageLoaderクラス
//////////////////////////////////////////////////
import flash.events.EventDispatcher;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.net.URLRequest;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.HTTPStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.system.LoaderContext;
class ImageLoader extends EventDispatcher {
private var loader:Loader;
public var id:uint;
private var info:LoaderInfo;
public var content:Bitmap;
private var smoothing:Boolean;
public static const IO_ERROR:String = IOErrorEvent.IO_ERROR;
public static const HTTP_STATUS:String = HTTPStatusEvent.HTTP_STATUS;
public static const SECURITY_ERROR:String = SecurityErrorEvent.SECURITY_ERROR;
public static const INIT:String = Event.INIT;
public static const COMPLETE:String = Event.COMPLETE;
public function ImageLoader() {
loader = new Loader();
info = loader.contentLoaderInfo;
}
public function load(file:String, s:Boolean = false):void {
smoothing = s;
info.addEventListener(IOErrorEvent.IO_ERROR, ioerror, false, 0, true);
info.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpstatus, false, 0, true);
info.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityerror, false, 0, true);
info.addEventListener(Event.INIT, initialize, false, 0, true);
info.addEventListener(Event.COMPLETE, complete, false, 0, true);
try {
//loader.load(new URLRequest(file));
loader.load(new URLRequest(file), new LoaderContext(true));
} catch (err:Error) {
trace(err.message);
}
}
public function unload():void {
loader.unload();
}
private function ioerror(evt:IOErrorEvent):void {
loader.unload();
dispatchEvent(new Event(ImageLoader.IO_ERROR));
}
private function httpstatus(evt:HTTPStatusEvent):void {
dispatchEvent(new Event(ImageLoader.HTTP_STATUS));
}
private function securityerror(evt:SecurityErrorEvent):void {
dispatchEvent(new Event(ImageLoader.SECURITY_ERROR));
}
private function initialize(evt:Event):void {
if (smoothing) {
content = Bitmap(info.content);
content.smoothing = true;
} else {
content = Bitmap(info.content);
}
dispatchEvent(new Event(ImageLoader.INIT));
}
private function complete(evt:Event):void {
info.removeEventListener(IOErrorEvent.IO_ERROR, ioerror);
info.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpstatus);
info.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityerror);
info.removeEventListener(Event.INIT, initialize);
info.removeEventListener(Event.COMPLETE, complete);
dispatchEvent(new Event(ImageLoader.COMPLETE));
}
public function centerize():void {
content.x = -uint(content.width*0.5);
content.y = -uint(content.height*0.5);
}
}
//////////////////////////////////////////////////
// Brushクラス
//////////////////////////////////////////////////
import flash.display.Sprite;
import flash.display.Shape;
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.InterpolationMethod;
class Brush extends Sprite {
private var circle:Shape;
private var radius:Number = 12.5;
public function Brush() {
draw();
}
private function draw():void {
circle = new Shape();
addChild(circle);
var colors:Array = [0xFFFFFF, 0x000000];
var alphas:Array = [1, 1];
var ratios:Array = [0, 255];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(radius*2, radius*2, 0, 0, 0);
circle.graphics.beginGradientFill(GradientType.RADIAL, colors, alphas, ratios, matrix, SpreadMethod.PAD, InterpolationMethod.RGB, 0);
//circle.graphics.beginFill(0x000000);
circle.graphics.drawRect(0, 0, radius*2, radius*2);
circle.graphics.endFill();
}
}