![Thumbnail : Dolphin Play [Alternativa3D7.5 Sample]](https://d12wcjmk7c4d40.cloudfront.net/archives/da311a0c916a0e01f9751306d5ca9b68898dde78.jpg)
package { import alternativ7.engine3d.animation.AnimationController; import alternativ7.engine3d.animation.AnimationGroup; import alternativ7.engine3d.animation.AnimationState; import alternativ7.engine3d.animation.AnimationTimer; import alternativ7.engine3d.animation.keys.MatrixKey; import alternativ7.engine3d.animation.MatrixAnimation; import alternativ7.engine3d.animation.Track; import alternativ7.engine3d.containers.ConflictContainer; import alternativ7.engine3d.controllers.SimpleObjectController; import alternativ7.engine3d.core.Camera3D; import alternativ7.engine3d.core.Object3DContainer; import alternativ7.engine3d.core.Sorting; import alternativ7.engine3d.core.View; import alternativ7.engine3d.loaders.MaterialLoader; import alternativ7.engine3d.loaders.ParserCollada; import alternativ7.engine3d.materials.FillMaterial; import alternativ7.engine3d.materials.TextureMaterial; import alternativ7.engine3d.objects.Joint; import alternativ7.engine3d.objects.Skin; import alternativ7.engine3d.primitives.Box; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.GradientType; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.net.URLLoader; import flash.net.URLRequest; import flash.system.LoaderContext; import flash.text.TextField; import flash.text.TextFormat; import flash.ui.Keyboard; [SWF(backgroundColor="#000000", frameRate="60", width="800", height="600")] public class Main extends Sprite { private const modelURL:String = "http://marubayashi.net/archive/sample/alt3d7/dolphin.dae"; private var loader:URLLoader; private var materialLoader:MaterialLoader; private var back:Sprite private var backMaterial:TextureMaterial; private var camera:Camera3D; private var cameraController:SimpleObjectController; private var moveController:dolphinController; private var animationController:AnimationController; private var animationTimer:AnimationTimer private var container:ConflictContainer; private var anim:Boolean = true; private var dolphinObject:ConflictContainer; private var frame:Sprite = new Sprite(); private var textures:Vector.<TextureMaterial> private var nextX:Number=0 private var nextY:Number=0 private var nextZ:Number=0 private var viewLength:Number private var focalLength:Number private var tField:TextField private var tFormat:TextFormat private var light:Sprite private var dolphin:Skin private var info:TextInfo private var scale:Number=0.5 private var context :LoaderContext = new LoaderContext(); public function Main() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.quality = StageQuality.HIGH; // Security.loadPolicyFile("http://marubayashi.net/crossdomain.xml"); // context.applicationDomain = ApplicationDomain.currentDomain; //--------------------------------- // Root objectの作成 //--------------------------------- container = new ConflictContainer(); //--------------------------------- //背景の作成 //--------------------------------- //Caustics(水面、底面)Materialの作成 back = new water() back.visible=false addChild(back) backMaterial = new TextureMaterial(new BitmapData(260, 860, false, 0x000033)); backMaterial.texture.draw(back) //backMaterial.mipMapping = MipMapping.PER_PIXEL; //backMaterial.smooth = true; var blackMaterial:FillMaterial = new FillMaterial(0x0,0); // Box var box:Box = new Box(6000, 8000, 1400, 1, 1, 1, true,false,blackMaterial,blackMaterial,blackMaterial,blackMaterial,backMaterial,backMaterial); //box.setMaterialToAllFaces(backMaterial); container.addChild(box); box.y=2000 box.blendMode=BlendMode.SCREEN box.rotationZ = 180 * Math.PI / 180 box.sorting = Sorting.AVERAGE_Z; //--------------------------------- //手前光の描画 //--------------------------------- light = new Sunlight() addChild(light) //--------------------------------- //cameraの作成 //--------------------------------- camera = new Camera3D(); camera.view = new View(800, 600); addChild(camera.view); camera.rotationX = -90*Math.PI/180; //camera.rotationZ = -10*Math.PI/180; camera.x = -0; camera.y = -500; camera.z = -0; container.addChild(camera); addChild(frame); loader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onModelLoad); loader.load(new URLRequest(modelURL)); // Info info = new TextInfo(); info.x = 3; info.y = 3; addChild(info); onResize(); } private function onModelLoad(e:Event):void { //イルカを格納するContainer dolphinObject = new ConflictContainer(); dolphinObject.resolveByAABB = true; dolphinObject.resolveByOOBB = true; dolphinObject.scaleX=scale dolphinObject.scaleY=scale dolphinObject.scaleZ=scale container.addChild(dolphinObject) //---------------------------------------------------- //Blenderが吐き出すDaeファイルでは、以下の置換が必要 //---------------------------------------------------- var myPattern:RegExp = /IDREF/g; loader.data=loader.data.replace(myPattern, "Name"); //---------------------------------------------------- //パース //---------------------------------------------------- var collada:ParserCollada = new ParserCollada(); //collada.parse(XML(loader.data), modelURL); collada.parseForAnimation(XML(loader.data), modelURL); //collada.parseForStatic(XML(loader.data), modelURL); var objects:* = collada.objects; //---------------------------------------------------- //objectのadd //---------------------------------------------------- var bones:Object3DContainer var boneY:Number=0 var count:int = objects.length; for (var o:int = 0; o < count; o++) { if (objects[o].name == 'Camera') { //trace(objects[o].name,o) } else { dolphinObject.addChild(objects[o]); dolphin = objects[o] as Skin dolphin.sorting = Sorting.AVERAGE_Z; dolphin.rotationZ = -90 * Math.PI / 180 dolphin.y=100 } } //---------------------------------------------------- //どうもボーンの回転が、基点軸回転ではない(Translation後Rotationしてる?) //ようなのでアニメも自作する。また、逆行列の扱いがblenderが吐き出す物と違うため、 //ボーンの始点を動かすと、スキンが伸びてしまう。 //なので、ボーンの再配置からやり直す。 //Dae側で、ルートボーン以下を並列で配置しておく //(つなげていると呼び出すのがめんどくさい) //---------------------------------------------------- var joint:Joint joint = dolphin.getJointAt(0); //アニメシーケンスの作成 var matrixAnimations:Vector.<MatrixAnimation> = new Vector.<MatrixAnimation>() matrixAnimations[0] = new MatrixAnimation(joint.getJointAt(0)) matrixAnimations[1] = new MatrixAnimation(joint.getJointAt(1)) matrixAnimations[2] = new MatrixAnimation(joint.getJointAt(2)) //Trackはコンストラクタで初期化されていないので、自前で作成 var track1:Track = new Track(); var track2:Track = new Track(); var track3:Track = new Track(); matrixAnimations[0].matrix = track1 matrixAnimations[1].matrix = track2 matrixAnimations[2].matrix = track3 var keyTime:Vector.<Number> = Vector.<Number>([0.04000, 0.44000, 0.84000, 1.24000, 1.64000]); var boneXs:Vector.<Number> = Vector.<Number>([120,200,280]); var rounds:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>() var rot1:Number=5 var rot2:Number=10 var rot3:Number=40 rounds[0] = Vector.<Number>([0, rot1, 0, -rot1, 0]); rounds[1] = Vector.<Number>([0, rot2, 0, -rot2, 0]); rounds[2] = Vector.<Number>([0, rot3, 0, -rot3, 0]); var trans:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>() trans[0] = Vector.<Number>([0, 0, 0, 0, 0]); trans[1] = Vector.<Number>([0, Math.sin(rot1*Math.PI/180)*120, 0, -Math.sin(rot1*Math.PI/180)*120, 0]); trans[2] = Vector.<Number>([0, Math.sin(rot2*Math.PI/180)*80+Math.sin(rot1*Math.PI/180)*120, 0, -Math.sin(rot2*Math.PI/180)*80-Math.sin(rot1*Math.PI/180)*120, 0]); for (var m:int = 0; m < 3 ;m++ ) { for (var t:int = 4; t >=0 ;t-- ) { var animeMatrix:Matrix3D = new Matrix3D() animeMatrix.prependTranslation(boneXs[m],0,0); animeMatrix.prependRotation(rounds[m][t], Vector3D.Z_AXIS); animeMatrix.prependTranslation(-boneXs[m], trans[m][t],0); matrixAnimations[m].matrix.addKey(new MatrixKey(keyTime[t], animeMatrix)) } //ループする時間を設定(これがないと動かない) matrixAnimations[m].length = 1.64 } //出来たアニメはAnimationグループへ var animations:AnimationGroup = new AnimationGroup(dolphin); animations.addAnimation(matrixAnimations[0]); animations.addAnimation(matrixAnimations[1]); animations.addAnimation(matrixAnimations[2]); //ループする時間を設定(これがないと動かない) //animations.length=1.64 animationController = new AnimationController() //var animation:Animation = collada.getAnimationByObject(objects[0]); animationController.addAnimation('tail',animations, true); animationController.playAll() animationTimer = new AnimationTimer() animationTimer.addController(animationController) animationTimer.start() //---------------------------------------------------- // Camera controller //---------------------------------------------------- cameraController = new SimpleObjectController(stage, camera, 200); cameraController.mouseSensitivity = 0 cameraController.unbindAll() camera.debug = true; //camera.farClipping=2 addChild(camera.diagram); //camera.addToDebug(Debug.BONES,objects[0]) //camera.addToDebug(Debug.EDGES,objects[0]) //---------------------------------------------------- //イルカコントローラー //---------------------------------------------------- moveController = new dolphinController(stage, dolphinObject, 400*scale, 5); //moveController.mouseSensitivity=0 moveController.unbindAll() moveController.bindKey(87, SimpleObjectController.ACTION_FORWARD); moveController.bindKey(Keyboard.UP, SimpleObjectController.ACTION_PITCH_DOWN); moveController.bindKey(90, SimpleObjectController.ACTION_BACK); moveController.bindKey(Keyboard.DOWN, SimpleObjectController.ACTION_PITCH_UP); moveController.bindKey(65, SimpleObjectController.ACTION_YAW_LEFT); moveController.bindKey(Keyboard.LEFT, SimpleObjectController.ACTION_YAW_LEFT); moveController.bindKey(68, SimpleObjectController.ACTION_YAW_RIGHT ); moveController.bindKey(Keyboard.RIGHT, SimpleObjectController.ACTION_YAW_RIGHT); dolphinObject.rotationZ = 90 * Math.PI / 180 moveController.bindKey(Keyboard.SHIFT,SimpleObjectController.ACTION_ACCELERATE); moveController.bindKey(Keyboard.ENTER,SimpleObjectController.ACTION_ACCELERATE); //---------------------------------------------------- //Texture作成 //---------------------------------------------------- //materialLoaderへはVectorで渡す //Loadが終わると勝手に貼り付けてくれる。 textures = new Vector.<TextureMaterial>(1); textures[0] = new TextureMaterial(); textures[0] = new TextureMaterial(); textures[0].diffuseMapURL='http://marubayashi.net/archive/sample/alt3d7/texture.jpg' dolphin.setMaterialToAllFaces(textures[0]) materialLoader = new MaterialLoader(); materialLoader.addEventListener(Event.COMPLETE, onMaterialsLoad); materialLoader.load(textures); } private var state:AnimationState private function onMaterialsLoad(e:Event):void { //MaterialのLoad処理終了 addEventListener(Event.ENTER_FRAME, onEnterFrame); stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); stage.addEventListener(Event.RESIZE, onResize); } public function onEnterFrame(e:Event = null):void { if (dolphinObject != null) { var ty:Number = dolphinObject.z / (dolphinObject.y + focalLength) * focalLength var tx:Number = dolphinObject.x / (dolphinObject.y + focalLength) * focalLength var size:Number = 400*scale / (dolphinObject.y + focalLength) * focalLength if (dolphinObject.y < -2000 || Math.abs(tx) > camera.view.width / 2 + size || Math.abs(ty) > camera.view.height / 2 + size ) { moveController.lookAtXYZ(0, 2000, 0) moveController.onKeyUp(); } if (anim) { moveController.moveForward(true); } else { moveController.moveForward(false); } } else { } moveController.update(); animationTimer.update() animationController.update(0.005); cameraController.update(); camera.render(); backMaterial.texture.draw(back) } private function onKeyDown(e:KeyboardEvent):void { switch (e.keyCode) { case Keyboard.TAB: camera.debug = !camera.debug; break; case Keyboard.ENTER: moveController.lookAtXYZ(0, 0, 0) break; case Keyboard.SPACE: anim = !anim; break; case 81: // Q if (stage.quality == "HIGH") { stage.quality = StageQuality.LOW; } else { stage.quality = StageQuality.HIGH; } break; } } public function onResize(e:Event = null):void { var pd:Number = 80; camera.view.width = stage.stageWidth// - pd*2; camera.view.height = stage.stageHeight// - pd*2; camera.view.x = 0//pd; camera.view.y = 0//pd; var colors:Array=new Array(0xDDEEFF,0x003366,0x000000,0x000000,0x002266,0x9999FF) var alphas:Array=new Array(1,1,1,1,1,1) var ratios:Array=new Array(0,100,140,150,180,255) var matrix:Matrix=new Matrix() matrix.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0) graphics.clear() graphics.beginGradientFill(GradientType.LINEAR,colors, alphas, ratios, matrix) graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight) //cameraからイルカまでの距離 var d:Vector3D=new Vector3D(dolphinObject.x,dolphinObject.y,dolphinObject.z) var c:Vector3D=new Vector3D(0,-500,0) var distance:Number = Vector3D.distance(c,d); camera.fov = 2 * Math.atan2(Math.sqrt(camera.view.width * camera.view.width +camera.view.height * camera.view.height) / 2,distance); } } } //----------------------------------------------------------------- //イルカController //なんちゃって当たり判定付き //----------------------------------------------------------------- import alternativ7.engine3d.controllers.SimpleObjectController; import alternativ7.engine3d.core.Object3D; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.BlendMode; import flash.display.GradientType; import flash.display.Graphics; import flash.display.InteractiveObject; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageQuality; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.TimerEvent; import flash.filters.BlurFilter; import flash.filters.DisplacementMapFilter; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.geom.Vector3D; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.Timer; class dolphinController extends SimpleObjectController{ public var _target:Object3D private var _yawSpeed:Number= 1.5 private var _pitchSpeed:Number = 2 public var _pitchDown:Boolean public var _pitchUp:Boolean public var _yawLeft:Boolean public var _yawRight:Boolean public var _yawNear:Boolean=false public var _yawNearAngle:Number public var _pitchNear:Boolean=false public var _pitchNearAngle:Number public function dolphinController(param0:InteractiveObject, param1:Object3D, param2:Number, param3:Number = 3, param4:Number = 1) { _target=param1 super(param0, param1, param2, param3, param4); param0.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); param0.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); } public function onKeyDown(e:KeyboardEvent):void { for (var key:String in keyBindings) { if (String(e.keyCode)==key) { keyBindings[key](true) } } } public function onKeyUp(e:KeyboardEvent = null):void { for (var key:String in keyBindings) { /* if (e) { if (String(e.keyCode)==key) { keyBindings[key](false) } } else { }*/ keyBindings[key](false) } _yawNear = false _pitchNear = false; } override public function bindKey(param0:uint, param1:String): void { switch (param1) { case ACTION_YAW_LEFT: keyBindings[param0]=yawLeft break case ACTION_YAW_RIGHT: keyBindings[param0]=yawRight break case ACTION_PITCH_DOWN: keyBindings[param0]=pitchDown break case ACTION_PITCH_UP: keyBindings[param0]=pitchUp break } super.bindKey(param0, param1) } public function pitchDown(value:Boolean):void { _pitchDown=value } public function pitchUp(value:Boolean):void { _pitchUp=value } public function yawLeft(value:Boolean,near:Boolean=false):void { _yawLeft = value if (near) { _yawNear = true } } public function yawRight(value:Boolean,near:Boolean=false):void { _yawRight = value if (near) { _yawNear = true } } override public function update(): void { //現在のVector3D var nowVector:Vector3D=new Vector3D(_target.x,_target.y,_target.z) if (_yawLeft) { _target.rotationZ = (((_target.rotationZ / Math.PI * 180) - _yawSpeed) % 360) * Math.PI/180 } else if (_yawRight) { _target.rotationZ = (((_target.rotationZ / Math.PI * 180) + _yawSpeed) % 360) * Math.PI/180 } if (_pitchDown) { _target.rotationX=(((_target.rotationX / Math.PI * 180) + _pitchSpeed) % 360) * Math.PI/180 } if (_pitchUp) { _target.rotationX=(((_target.rotationX / Math.PI * 180) - _pitchSpeed) % 360) * Math.PI/180 } updateObjectTransform() super.update() //Colsion関係がまだ実装されてないので、Controller内で無理やり対応 //Vector3Dで処理せんといかん //現在の進行方向をVector3Dに記憶 var nextVector:Vector3D = new Vector3D(_target.x, _target.y, _target.z) //現在の進行方向 var velocity:Vector3D = nextVector.subtract(nowVector); var normalVelocity:Vector3D if (_target.y > 30000) { normalVelocity = collision(velocity, new Vector3D(0, 1, 0)) lookAt(nextVector.add(normalVelocity)) _target.y = 29999 } if (_target.z > 10000) { //上なので、下向きベクトル //normalVelocity = collision(velocity, new Vector3D(0, 0, -1)) //lookAt(nextVector.add(normalVelocity)) _target.rotationX=(((_target.rotationX / Math.PI * 180) - _pitchSpeed) % 360) * Math.PI/180 _target.z=9999 } else if (_target.z < -1000) { //normalVelocity = collision(velocity, new Vector3D(0, 0, 1)) //lookAt(nextVector.add(normalVelocity)) _target.rotationX=(((_target.rotationX / Math.PI * 180) + _pitchSpeed) % 360) * Math.PI/180 _target.z = -999 } } public function collision(velocity:Vector3D, faceNormal:Vector3D):Vector3D { //衝突面のnormalと進行方向の内積で新たな方向を出す //進行方向を反転させる var reverse:Vector3D //Faceの法線を正規化 var tempVector:Vector3D=faceNormal.clone() tempVector.normalize() //進行スピードを反転する reverse = velocity.clone() reverse.negate() //投影の長さを計算 //投影の長さに面法線ベクトル*衝突後の速度をかける //tempVector.scaleBy(2*tempVector.dotProduct(reverse)) tempVector.scaleBy(2*tempVector.dotProduct(reverse)/10) return velocity.add(tempVector); } public function set yawSpeed(value:Number):void { _yawSpeed = value*Math.PI/180; } public function set pitchSpeed(value:Number):void { _pitchSpeed = value*Math.PI/180; } } class TextInfo extends TextField { public function TextInfo() { autoSize = TextFieldAutoSize.LEFT; selectable = false; defaultTextFormat = new TextFormat("Tahoma", 10, 0x7F7F7F); } public function write(value:String):void { appendText(value + "\n"); } public function clear():void { text = ""; } } /** * 海面から射す光 * * @author narutohyper */ class Sunlight extends Sprite { private var angle:uint = 0 private var light:Sprite=new Sprite() private var yuragi:BitmapData; public function Sunlight() { makelight() addEventListener(Event.ADDED_TO_STAGE, init); } private function init(event : Event) : void { stage.addEventListener(Event.RESIZE, onResize); onResize() } private function makelight():void { var angleArray:Array = new Array() var lightArray:Array = new Array() while (angle < 360) { angle += Math.floor(Math.random() * 5) angleArray.push(angle) } var rx1:Number var ry1:Number var rx2:Number var ry2:Number var colors:Array=new Array(0xFFFFFF,0xFFFFFF) var alphas:Array=new Array(1,0) var ratios:Array=new Array(100,255) var matrix:Matrix = new Matrix(); matrix.createGradientBox(400,400,0,-200,-200) light.graphics.beginGradientFill(GradientType.RADIAL,colors, alphas, ratios, matrix) for (var i:uint = 0; i < angleArray.length - 3; i += 3) { if (angleArray[i]>0 && angleArray[i]<90) { rx1=Math.sin(angleArray[i]*Math.PI/180) ry1=Math.cos(angleArray[i]*Math.PI/180) rx2=Math.sin(angleArray[i+1]*Math.PI/180) ry2=Math.cos(angleArray[i+1]*Math.PI/180) light.graphics.moveTo(rx1*10,ry1*10) light.graphics.lineTo(rx2*10,ry2*10) light.graphics.lineTo(rx2*200,ry2*200) light.graphics.lineTo(rx1 * 200, ry1 * 200) } } light.graphics.endFill() light.x=0 light.y=0 var f1:BlurFilter = new BlurFilter(20, 20, 1); light.blendMode=BlendMode.ADD light.alpha = 0.2 addChild(light) light.filters = [f1]; } private function createMap(width:int, height:int):BitmapData{ var bmp:BitmapData = new BitmapData(width, height, false, 0xffffff); for(var j:int = 0; j < height; j++){ for(var i:int = 0; i < width; i++){ bmp.setPixel(i, j, 32*Math.sin(Math.PI*3/height*j)+128+64); } } return bmp; } public function onResize(e:Event = null):void { height=stage.stageHeight*1.5 width=stage.stageHeight*1.5 //light.x = 400 //light.y= 300 y=stage.stageHeight/-2 } } /** * 以下は、 * Caustics(水面、底面)Materialの作成 * * * 超速ボロノイ図(Fortune's algorithm) * http://wonderfl.net/c/3TKq * * を使用させていただいてます。 * * 正直、なにやってるかわかりませんw * * */ /** * 超速ボロノイアルゴリズム * 元ネタ * Fortune's algorithm - Wikipedia, the free encyclopedia * http://en.wikipedia.org/wiki/Fortune's_algorithm * Controul > Speedy Voronoi diagrams in as3/flash * http://blog.controul.com/2009/05/speedy-voronoi-diagrams-in-as3flash/ * 上記blogのasそのままです(Fortuneクラス)。 * 全然ロジックわからないので誰か解説してほしいです。 * * 母点を800程度にしてますがもう少し多くてもいけそう。 * fullscreenでもお楽しみいただけます(重いけど) * * 日本語の資料(PDF) * http://atom.is.ocha.ac.jp/~kanenko/KOUGI/CompGeo/cpgeob.pdf * http://i-health.u-aizu.ac.jp/CompuGeo/2008/handouts/chapter4/Chapter4H.pdf */ //import com.flashdynamix.utils.SWFProfiler; class water extends Sprite { //キャンパス private var _gradation : Sprite; private var _canvas : Sprite; //background private var _background:BitmapData; //voronoi母点数 private const Q : uint = 200; //voronoi作図用のクラス private var fortune : Fortune; //voronoi母点(パーティクル的な?) private var points : Vector.<Number2>; private var _first : Number2; private var stageWidth : int=300; private var stageHeight : int=300; private var yuragi:BitmapData; private var bmd:BitmapData; private var bm:Bitmap; private var bmd2:BitmapData; private var bm2:Bitmap; public function water() { addEventListener(Event.ADDED_TO_STAGE, _initialize); } /** * 初期化 */ private function _initialize(event : Event) : void { var i : uint,old : Number2,point : Number2; removeEventListener(Event.ADDED_TO_STAGE, _initialize); //SWFProfiler.init(this); //背景カラー bmd = new BitmapData(stageWidth , 900, false, 0x0); bm=new Bitmap(bmd) bmd2 = new BitmapData(stageWidth-40 , 860, false, 0x0); yuragi = createMap3(stageWidth , 900) //bm2=new Bitmap(yuragi) bm2=new Bitmap(bmd2) addChild(bm2); //キャンパス設定 _canvas = new Sprite(); _gradation = new Sprite() addChild(_gradation); var colors:Array=new Array(0xFFFFFF,0x000000) var alphas:Array=new Array(1,1) var ratios:Array=new Array(0,160) var matrix:Matrix=new Matrix() matrix.createGradientBox(stageWidth-40, 860, Math.PI / 2, 0, 0) _gradation.graphics.clear() _gradation.graphics.beginGradientFill(GradientType.LINEAR,colors, alphas, ratios, matrix) _gradation.graphics.drawRect(0,0,stageWidth-40,860) _gradation.blendMode=BlendMode.MULTIPLY //voronoi母点生成 points = new Vector.<Number2>(); for(i = 0;i < Q;i++) { point = new Number2(); point.x = stageWidth * Math.random(); point.y = stageHeight * Math.random(); //各母点の速度 point.vx = (Math.random()*2-1)*0.5; point.vy = (Math.random()*2-1)*0.5; points.push(point); } //voronoi母点リンクリスト生成 for(i = 0;i < Q;i++) { point = points[i]; if (_first == null) { old = _first = point; } else { old.next = point; old = point; } } //ボロノイ作図用クラス fortune = new Fortune(); //addEventListener(Event.ENTER_FRAME, _updateHandler); var timer:Timer = new Timer(60, 0) timer.addEventListener(TimerEvent.TIMER, _updateHandler); timer.start() } private function createMap3(width:int, height:int):BitmapData{ var bmp:BitmapData = new BitmapData(width, height, false, 0xffffff); for(var j:int = 0; j < height; j++){ for(var i:int = 0; i < width; i++){ bmp.setPixel(i, j, 128*Math.sin(Math.PI*10/height*j)); } } return bmp; } //アップデート private function _updateHandler(event : Event) : void { _interaction(); _draw(); //removeEventListener(Event.ENTER_FRAME, _updateHandler); var f1:DisplacementMapFilter = new DisplacementMapFilter(yuragi, new Point(0,0), BitmapDataChannel.BLUE, BitmapDataChannel.BLUE, 100, 0, "color", 0x0); var f2:BlurFilter = new BlurFilter(6, 6, 1); bmd.fillRect(new Rectangle(0, 0, 300, 900), 0x000000); bmd.draw(_canvas,new Matrix(1, 0, 0, 1, 0, 250), null, null, new Rectangle(0, 250, 300, 650)); bm.filters = [f1,f2]; bmd2.draw(bm, new Matrix(1, 0, 0, 1, -20, -20), null, null, new Rectangle(0, 0, 260, 880)); } //インタラクション private function _interaction() : void { var point : Number2 = _first; do { //母点の位置を更新 point.x += point.vx; point.y += point.vy; if(point.x > stageWidth) { point.x -= stageWidth; }else if(point.x < 0) { point.x += stageWidth; } if(point.y > stageHeight) { point.y -= stageHeight; }else if(point.y < 0) { point.y += stageHeight; } } while (point = point.next); //更新した母点をvoronoi作図用クラスに入れる fortune.points = points; } //描画 private function _draw() : void { //ボロノイ頂点 var segments : Vector.<Number2> = fortune.compute(), i : uint,start : Number2,end : Number2, point : Number2 = _first, g : Graphics = _canvas.graphics; //ボロノイ辺の描画 g.clear(); g.beginFill(0x000000) g.drawRect(0,0,300,300) g.lineStyle(5, 0xFFFFFF, 1); for(i = 0;i < segments.length;i += 2) { start = segments[i]; end = segments[i + 1]; g.moveTo(start.x, start.y); g.lineTo(end.x, end.y); } } } /* * Fortune's algorithm * http://blog.controul.com/2009/05/speedy-voronoi-diagrams-in-as3flash/ * オリジナルは上記blogからダウンロードして見てください! * はっきりいって全然わからないのでなにやってるのかどなたか解説を! */ class Fortune { // voronoi図の母点となる点群 public var points : Vector.<Number2>; // Bounding box. private var x0 : Number; // Root of the frontline and next arc to be removed. private var root : Arc; private var next : Arc; // Reusable objects and pools. private var o : Number2 = new Number2; private static var arcPoolD : Arc; /** * 与えられた母点からvoronoi頂点群を返します. * @return A vector or vertices in pairs, describing segments ready for drawing. */ public function compute() : Vector.<Number2> { // Clear the output. var out : Vector.<Number2> = new Vector.<Number2>, len : int = 0; // Clear the state. root = null; next = null; // Read the pools. var key : * , arcPool : Arc = arcPoolD; // Vars: var i : int, j : int, w : Number, x : Number, a : Arc, b : Arc, z : Number2, p : Number2 = points[ 0 ], points : Vector.<Number2> = points, n : int = points.length, // Circle events check inlined. circle : Boolean, eventX : Number, c : Arc, d : Arc, aa : Number2, bb : Number2, cc : Number2, A : Number, B : Number, C : Number, D : Number, E : Number, F : Number, G : Number; // 与えられた母点をx軸でソート ///// Currently insertion sort. Quicksort? w = points[ 0 ].x; for ( i = 1;i < n;i++ ) { p = points[ i ]; // Insertion sort. x = p.x; if ( x < w ) { j = i; while ( ( j > 0 ) && ( points[ int(j - 1) ].x > x ) ) { points[ j ] = points[ int(j - 1) ]; j--; } points[ j ] = p; } else w = x; } // Get x bounds. x0 = points[ 0 ].x; // Process. i = 0; p = points[ 0 ]; x = p.x; //多分母点群でループ for ( ;; ) { // Check circle events. ///////////////////////// if ( a ) { // Check for arc a. circle = false; if ( a.prev && a.next ) { aa = a.prev.p, bb = a.p, cc = a.next.p; // Algorithm from O'Rourke 2ed p. 189. A = bb.x - aa.x, B = bb.y - aa.y, C = cc.x - aa.x, D = cc.y - aa.y; // Check that bc is a "right turn" from ab. if ( A * D - C * B <= 0 ) { E = A * ( aa.x + bb.x ) + B * ( aa.y + bb.y ), F = C * ( aa.x + cc.x ) + D * ( aa.y + cc.y ), G = 2 * ( A * ( cc.y - bb.y ) - B * ( cc.x - bb.x ) ); // Check for colinearity. // if ( G > 0.000000001 || G < -0.000000001 ) if ( G ) { // Point o is the center of the circle. o.x = ( D * E - B * F ) / G; o.y = ( A * F - C * E ) / G; // o.x plus radius equals max x coordinate. A = aa.x - o.x; B = aa.y - o.y; eventX = o.x + Math.sqrt(A * A + B * B); if ( eventX >= w ) circle = true; } } } // Remove from queue. if ( a.right ) a.right.left = a.left; if ( a.left ) a.left.right = a.right; if ( a == next ) next = a.right; // Record event. if ( circle ) { a.endX = eventX; if ( a.endP ) { a.endP.x = o.x; a.endP.y = o.y; } else { a.endP = o; o = new Number2; } d = next; if ( !d ) { next = a; } else for ( ;; ) { if ( d.endX >= eventX ) { a.left = d.left; if ( d.left ) d.left.right = a; if ( next == d ) next = a; a.right = d; d.left = a; break; } if ( !d.right ) { d.right = a; a.left = d; a.right = null; break; } d = d.right; } } else { a.endX = NaN; a.endP = null; a.left = null; a.right = null; } // Push next arc to check. if ( b ) { a = b; b = null; continue; } if ( c ) { a = c; c = null; continue; } a = null; } ////////////////////////////////////////////////// // if ( next && next.endX <= x ) { // // Handle next circle event. // Get the next event from the queue. /////////// a = next; next = a.right; if ( next ) next.left = null; a.right = null; // Remove the associated arc from the front. if ( a.prev ) { a.prev.next = a.next; a.prev.v1 = a.endP; } if ( a.next ) { a.next.prev = a.prev; a.next.v0 = a.endP; } if ( a.v0 ) { out[ len++ ] = a.v0; a.v0 = null; out[ len++ ] = a.endP; } if ( a.v1 ) { out[ len++ ] = a.v1; a.v1 = null; out[ len++ ] = a.endP; } // Keep a ref for collection. d = a; // Recheck circle events on either side of p: w = a.endX; if ( a.prev ) { b = a.prev; a = a.next; } else { a = a.next; b = null; } c = null; // Collect. d.v0 = null; d.v1 = null; d.p = null; d.prev = null; d.endX = NaN; d.endP = null; d.next = arcPool; arcPool = d; ////////////////////////////////////////////////// // } else { if ( !p ) break; // // Handle next site event. ////////////////////// if ( !root ) { if ( arcPool ) { root = arcPool; arcPool = arcPool.next; root.next = null; } else root = new Arc; root.p = p; } else { z = new Number2; // Find the first arc with a point below p, // and start searching for the intersection around it. a = root.next; if ( a ) { while ( a.next ) { a = a.next; if ( a.p.y >= p.y ) break; } // Find the intersecting curve. intersection(a.prev.p, a.p, p.x, z); if ( z.y <= p.y ) { // Search for the intersection to the south of i. while ( a.next ) { a = a.next; intersection(a.prev.p, a.p, p.x, z); if ( z.y >= p.y ) { a = a.prev; break; } } } else { // Search for the intersection above i. a = a.prev; while ( a.prev ) { a = a.prev; intersection(a.p, a.next.p, p.x, z); if ( z.y <= p.y ) { a = a.next; break; } } } } else a = root; // New parabola will intersect arc a. Duplicate a. if ( a.next ) { if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = a.p; b.prev = a; b.next = a.next; a.next.prev = b; a.next = b; } else { if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = a.p; b.prev = a; a.next = b; } a.next.v1 = a.v1; // Find the point of intersection. z.y = p.y; z.x = ( a.p.x * a.p.x + ( a.p.y - p.y ) * ( a.p.y - p.y ) - p.x * p.x ) / ( 2 * a.p.x - 2 * p.x ); // Add p between i and i->next. if ( arcPool ) { b = arcPool; arcPool = arcPool.next; b.next = null; } else b = new Arc; b.p = p; b.prev = a; b.next = a.next; a.next.prev = b; a.next = b; a = a.next; // Now a points to the new arc. a.prev.v1 = z; a.next.v0 = z; a.v0 = z; a.v1 = z; // Check for new circle events around the new arc: b = a.next; a = a.prev; c = null; w = p.x; } ////////////////////////////////////////////////// // i++; if ( i >= n ) { p = null; x = Number.MAX_VALUE; } else { p = points[ i ]; x = p.x; } } } // Store the pools. arcPoolD = arcPool; // // // Return the result ready for drawing. return out; } /** * Where do two parabolas intersect? * @param p0 A Number2 object describing the site for the first parabola. * @param p1 A Number2 object describing the site for the second parabola. * @param l The location of the sweep line. * @param res A Number2 object in which to store the intersection. * @return The point of intersection. */ public function intersection( p0 : Number2, p1 : Number2, l : Number, res : Number2 ) : Number2 { var p : Number2 = p0, ll : Number = l * l; if ( p0.x == p1.x ) res.y = ( p0.y + p1.y ) / 2; else if ( p1.x == l ) res.y = p1.y; else if ( p0.x == l ) { res.y = p0.y; p = p1; } else { // Use the quadratic formula. var z0 : Number = 0.5 / ( p0.x - l ); // 1 / ( 2*(p0.x - l) ) var z1 : Number = 0.5 / ( p1.x - l ); // 1 / ( 2*(p1.x - l) ) var a : Number = z0 - z1; var b : Number = -2 * ( p0.y * z0 - p1.y * z1 ); var c : Number = ( p0.y * p0.y + p0.x * p0.x - ll ) * z0 - ( p1.y * p1.y + p1.x * p1.x - ll ) * z1; res.y = ( -b - Math.sqrt(b * b - 4 * a * c) ) / ( 2 * a ); } // Plug back into one of the parabola equations. res.x = ( p.x * p.x + ( p.y - res.y ) * ( p.y - res.y ) - ll ) / ( 2 * p.x - 2 * l ); return res; } } class Arc { public var p : Number2; public var next : Arc; public var prev : Arc; public var v0 : Number2; public var v1 : Number2; // Circle event data : public var left : Arc; public var right : Arc; public var endX : Number; public var endP : Number2; } class Number2 { public var x : Number; public var y : Number; //速度 public var vx : Number; public var vy : Number; public var next : Number2; }