Fluid Water Simulation Alternativa3d 7
I’ve been trying to make use of a water effect in alternativa3d recently but haven’t had much luck finding any examples. I wasn’t sure where to start to make my own, so I decided to take an example that was made for papervision3d and away3d and try and impliment it in alternativa3d 7.7.0. The original example can be found here and the sample texture i’ve used can be found here. Anyway to set this up i decided to take the predefined Plane in alternativa and extend it to create a fluidplane which would do the work for us all we would need to call is the makeSplash function just like the original example. Here is the FluidPlane.as code
// A conversion of the papervision and away3d example
// http://exey.ru/blog/home/fluid-simulation-pv3d-and-away3d
package
{
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.primitives.Plane;
import alternativa.engine3d.materials.Material;
import flash.geom.Vector3D;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Canvas;
import alternativa.engine3d.core.Vertex;
import alternativa.engine3d.core.Face;
use namespace alternativa3d;
public class FluidPlane extends Plane
{
private var theta:Number = 0;
private var buffer:Array = new Array(2);
private var renderBuffer:int = 0;
private var k1:Number;
private var k2:Number;
private var k3:Number;
private var range:int = 20;
private var waterSegsX:int = range;
private var waterSegsY:int = range;
private var waterSegsXreal:int = waterSegsX+1;
private var waterSegsYreal:int = waterSegsY+1;
private var waterXcenter:int = range/2;
private var waterYcenter:int= range/2;
private var isPause:Boolean = false;
private var camera:Camera3D;
public function FluidPlane(camera:Camera3D,width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, twoSided:Boolean = true, reverse:Boolean = false, triangulate:Boolean = false, bottom:Material = null, top:Material = null)
{
this.camera = camera;
widthSegments = waterSegsX
lengthSegments = waterSegsY
super(width, length, widthSegments, lengthSegments, twoSided, reverse, triangulate, bottom, top);
prepareMath();
}
override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas) : void
{
super.draw(camera, parentCanvas);
theta += 0.05;
var verCounter:uint = 0;
for(var x:int = 0 ; x < waterSegsXreal ; x++){
for(var z:int = 0 ; z < waterSegsYreal ; z++){
this.vertices[verCounter].z = buffer[renderBuffer][x][z].y * 3;
verCounter++;
}
}
evaluate();
//calculate normals
this.calculateFacesNormals();
this.calculateVerticesNormals();
this.calculateBounds();
}
public function makeSplash(splashHeight:int):void
{
populateBuffer();
buffer[renderBuffer][waterXcenter][waterYcenter].y = splashHeight;
}
public function prepareMath():void
{
populateBuffer();
//trace(buffer[0].length, buffer[1].length)
var d:Number = 1;
var t:Number = 0.1;
var c:Number = 2.1;
var mu:Number = 0.001;
var f1:Number = c*c*t*t/(d*d);
var f2:Number = 1/(mu*t+2);
k1 = (4 - 8*f1)*f2;
k2 = (mu*t-2)*f2;
k3 = 2 * f1 * f2;
}
public function populateBuffer():void
{
var x:int,y:int,z:int;
// 3-d displacement will be stored in the array(2*w*h)
for(var i:int = 0 ; i < 2; i++)
{
var a:Array = new Array();
for(var j:int = 0 ; j < waterSegsYreal; j++)
{
var aa:Array = new Array();
for(var k:int = 0 ; k < waterSegsXreal; k++)
aa.push(new Vector3D(j,0,k));
a.push(aa);
}
buffer[i] = a;
}
}
private function evaluate():void
{
for(var j:int = 1 ; j < waterSegsYreal-1; j++)
{
var crnt:Array = buffer[renderBuffer][j];
var prev:Array = buffer[1-renderBuffer][j];
for (var i:int = 1 ; i < waterSegsXreal-1; i++)
{
// trace(j, i);
var currentN:Vector3D = (Vector3D)(buffer[renderBuffer][j + 1][i]);
var currentP:Vector3D = (Vector3D)(buffer[renderBuffer][j - 1][i]);
((Vector3D)(prev[i])).y = k1*((Vector3D)(crnt[i])).y + k2*((Vector3D)(prev[i])).y + k3*(((Vector3D)(crnt[i+1])).y + ((Vector3D)(crnt[i-1])).y + currentN.y + currentP.y);
}
}
renderBuffer = 1-renderBuffer;
}
}
}
Then to make use of this new plane i just used the following few lines to create the plane apply a texture and start a splash.
//material
var mat:TextureMaterial = new TextureMaterial(new FluidDiffuse().bitmapData);
//create fluidplane and apply material
var fplane:FluidPlane = new FluidPlane(camera);
fplane.calculateVerticesNormals(true, 0.01);
fplane.setMaterialToAllFaces(mat);
rootContainer.addChild(fplane);
//start a splash
fplane.makeSplash(10);
The result gave some effect to the geometry of the plane but it is no where near as smooth or good looking as the original. This is probably down to the bump mapping and lighting effects absent in my example. Here is the full class that called the fluidplane
package
{
import alternativa.engine3d.controllers.SimpleObjectController;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Debug;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Object3DContainer;
import alternativa.engine3d.core.View;
import alternativa.engine3d.materials.FillMaterial;
import alternativa.engine3d.materials.TextureMaterial;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
[SWF(backgroundColor="#000000", frameRate="100", width="800", height="600")]
public class test extends Sprite
{
[Embed(source="465-diffuse.jpg")] static private const FluidDiffuse:Class;
[Embed(source="465-normal.jpg")] static private const FluidNormal:Class;
private var rootContainer:Object3DContainer = new Object3DContainer();
private var camera:Camera3D;
private var controller:SimpleObjectController;
private var fplane:FluidPlane;
public function test():void
{
//setup stage
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//setup camera and controller
camera = new Camera3D();
camera.view = new View(stage.stageWidth, stage.stageHeight);
addChild(camera.view);
addChild(camera.diagram);
camera.y = -200;
camera.z = 150;
camera.lookAt(0,0,0);
controller = new SimpleObjectController(stage, camera, 200);
rootContainer.addChild(camera);
//setup material
var mat:TextureMaterial = new TextureMaterial(new FluidDiffuse().bitmapData);
//create fluidplane and apply material
fplane = new FluidPlane(camera);
fplane.calculateVerticesNormals(true, 0.01);
fplane.setMaterialToAllFaces(mat);
rootContainer.addChild(fplane);
//start a splash
fplane.makeSplash(10);
// Debug
camera.addToDebug(Debug.EDGES, Object3D);
camera.addToDebug(Debug.BOUNDS, Object3D);
// Listeners
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e:Event):void
{
controller.update();
camera.render();
}
private function onResize(e:Event = null):void {
camera.view.width = stage.stageWidth;
camera.view.height = stage.stageHeight;
}
}
}
Comments
Comments are currently closed