/* Copyright (c) 2011 EL-EMENT saharan * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation * files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, * modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package { import adobe.utils.CustomActions; import flash.system.LoaderContext; import flash.display.*; import flash.events.*; import flash.net.*; import flash.ui.ContextMenu; [SWF(frameRate = "30", width="440", height="440")] public class Water extends Sprite { private const SIZE:int = 440; private const NUM_DETAILS:int = 48; private const INV_NUM_DETAILS:Number = 1 / NUM_DETAILS; private const MESH_SIZE:Number = 100; private var count:uint; private var bmd:BitmapData; private var loader:Loader; private var vertices:Vector.; private var transformedVertices:Vector.; private var indices:Vector.; private var uvt:Vector.; private var size2:Number; private var heights:Vector.>; private var velocity:Vector.>; private var press:Boolean; [Embed(source = "env.png")] private var env:Class; public function Water():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); initSimulation(); contextMenu = new ContextMenu(); contextMenu.hideBuiltInItems(); size2 = SIZE / 2; count = 0; bmd = new env().bitmapData; addEventListener(Event.ENTER_FRAME, frame); stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:Event = null):void { drag(); press = true; }); stage.addEventListener(MouseEvent.MOUSE_UP, function(e:Event = null):void { press = false; }); stage.addEventListener(MouseEvent.MOUSE_MOVE, function(e:Event = null):void { if (press) drag(); } ); } private function initSimulation():void { vertices = new Vector.(NUM_DETAILS * NUM_DETAILS, true); transformedVertices = new Vector.(NUM_DETAILS * NUM_DETAILS * 2, true); indices = new Vector.(); uvt = new Vector.(NUM_DETAILS * NUM_DETAILS * 2, true); var i:int; var j:int; for (i = 2; i < NUM_DETAILS - 2; i++) { for (j = 2; j < NUM_DETAILS - 2; j++) { vertices[getIndex(j, i)] = new Vertex( (j - (NUM_DETAILS - 1) * 0.5) / NUM_DETAILS * MESH_SIZE, 0, (i - (NUM_DETAILS - 1) * 0.5) / NUM_DETAILS * MESH_SIZE); if (i != 2 && j != 2) { indices.push(getIndex(i - 1, j - 1), getIndex(i, j - 1), getIndex(i, j)); indices.push(getIndex(i - 1, j - 1), getIndex(i, j), getIndex(i - 1, j)); } } } heights = new Vector.>(NUM_DETAILS, true); velocity = new Vector.>(NUM_DETAILS, true); for (i = 0; i < NUM_DETAILS; i++) { heights[i] = new Vector.(NUM_DETAILS, true); velocity[i] = new Vector.(NUM_DETAILS, true); for (j = 0; j < NUM_DETAILS; j++) { heights[i][j] = 0; velocity[i][j] = 0; } } } private function getIndex(x:uint, y:uint):uint { return y * NUM_DETAILS + x; } private function frame(e:Event = null):void { stage.quality = StageQuality.LOW; count++; move(); updateMesh(); transformVertices(); draw(); } private function updateMesh():void { for (var i:int = 2; i < NUM_DETAILS - 2; i++) { for (var j:int = 2; j < NUM_DETAILS - 2; j++) { const index:int = j * NUM_DETAILS + i; vertices[index].y = heights[i][j] * 0.2; var nx:Number; // 法線ベクトルを計算 var ny:Number; nx = (heights[i][j] - heights[i - 1][j]) * 0.15; ny = (heights[i][j] - heights[i][j - 1]) * 0.15; var len:Number = 1 / Math.sqrt(nx * nx + ny * ny + 1); nx *= len; ny *= len; uvt[index * 2] = nx * 0.5 + 0.5 + ((i - NUM_DETAILS * 0.5) * INV_NUM_DETAILS * 0.25); // スフィアマップ uvt[index * 2 + 1] = ny * 0.5 + 0.5 + ((NUM_DETAILS * 0.5 - j) * INV_NUM_DETAILS * 0.25); } } } public function move():void { var i:int; var j:int; for (i = 1; i < NUM_DETAILS - 1; i++) { for (j = 1; j < NUM_DETAILS - 1; j++) { heights[i][j] += velocity[i][j]; if (heights[i][j] > 100) heights[i][j] = 100; else if (heights[i][j] < -100) heights[i][j] = -100; } } for (i = 1; i < NUM_DETAILS - 1; i++) { for (j = 1; j < NUM_DETAILS - 1; j++) { velocity[i][j] = (velocity[i][j] + (heights[i - 1][j] + heights[i][j - 1] + heights[i + 1][j] + heights[i][j + 1] - heights[i][j] * 4) * 0.4) * 0.95; // 周囲の平均の高さとの差 } } } public function drag():void { var i:int; var j:int; var mx:Number = mouseX / SIZE * NUM_DETAILS; var my:Number = (1 - mouseY / SIZE) * NUM_DETAILS; for (i = mx - 3; i < NUM_DETAILS - 1 && mx + 3; i++) { for (j = my - 3; j < NUM_DETAILS - 1 && my + 3; j++) { if (i > 1 && j > 1 && i < NUM_DETAILS - 1 && j < NUM_DETAILS - 1) { var len:Number = 3 - Math.sqrt((mx - i) * (mx - i) + (my - j) * (my - j)); if (len < 0) len = 0; velocity[i][j] -= len * (press ? 1 : 5); } } } } private function draw():void { graphics.clear(); graphics.beginFill(0); graphics.drawRect(0, 0, SIZE, SIZE); graphics.endFill(); graphics.beginBitmapFill(bmd); graphics.drawTriangles(transformedVertices, indices, uvt, TriangleCulling.POSITIVE); graphics.endFill(); } private function transformVertices():void { // 座標変換 var angle:Number = 70 * Math.PI / 180; var sin:Number = Math.sin(angle); var cos:Number = Math.cos(angle); for (var i:int = 0; i < vertices.length; i++) { var v:Vertex = vertices[i]; if(v) { var x:Number = v.x; // X軸回転 var y:Number = cos * v.y - sin * v.z; var z:Number = sin * v.y + cos * v.z; z = 1 / (z + 60); // カメラ座標 x *= z; // プロジェクション変換 y *= z; x = x * size2 + size2; // スクリーン座標 y = y * size2 + size2 * 0.85; transformedVertices[i << 1] = x; transformedVertices[(i << 1) + 1] = y; } } } } } class Vertex { public var x:Number; public var y:Number; public var z:Number; public function Vertex(x:Number, y:Number,z:Number) { this.x = x; this.y = y; this.z = z; } }