TUNAMI
3Dライブラリに入る前の3D基礎勉強その3。
3D照明の計算部分がまだちゃんと理解できてないですが。。
クリックで津波が起こせます。
/**
* Copyright rettuce ( http://wonderfl.net/user/rettuce )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/nNVw
*/
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.events.MouseEvent;
import caurina.transitions.Tweener;
[SWF(width = 465, height = 465, backgroundColor = 0x000000, frameRate = 60)]
/**
* ...
* @author rettuce
*
*/
public class Main extends Sprite
{
private var NUM:Number = 50; // 1辺のブロック数
private var WID:Number = 200; // ブロック幅
private var _pointArr:Array = [];
private var _triangleArr:Array = [];
private var _vpX:Number = stage.stageWidth/2; // 消失点
private var _vpY:Number = stage.stageHeight/2; // 消失点
private var _fl:Number = 330;
public function Main() {
init();
}
public function init():void
{
var N:Number = NUM+1;
/* 3DPoint 生成 */
for (var i:int = 0; i < N*N; i++) {
var xpos:Number = WID * (i % N) - (NUM)*WID/2;
var zpos:Number = -WID * Math.floor(i / N) + (NUM)*WID/2;
_pointArr[i] = new Point3D(xpos, _fl+WID*Math.random(), zpos);
}
for (i = 0; i < _pointArr.length; i++ ) {
_pointArr[i].setVanishingPoint(_vpX, _vpY); // 消失点の指定
_pointArr[i].setCenter(0, 1000, NUM*WID/2); // 中心点の指定
}
/* トライアングル 生成 */
var index:int = 0;
for (i = 0; i < N*NUM; i++) {
if ( (i % NUM)-Math.floor(i / N) != 0 || (i % N) == 0 ) {
_triangleArr[index] = new Triangle(_pointArr[i], _pointArr[i + N +1 ], _pointArr[i + N], 0x0066FF);
_triangleArr[index + 1] = new Triangle(_pointArr[i], _pointArr[i + 1], _pointArr[i + N +1] , 0x0066FF);
index += 2;
}
}
var light:Light = new Light();
for (i = 0; i < _triangleArr.length; i++ ) {
_triangleArr[i].light = light; // 3D照明
}
stage.addEventListener(MouseEvent.CLICK, clickHandler );
addEventListener(Event.ENTER_FRAME, enterframeHandler);
}
private var _cnt:int = 0;
private function clickHandler(e:MouseEvent):void
{
for (var i:int = 0; i < _pointArr.length; i++ ) {
if ( i % (NUM+1) == _cnt ) {
var t:Point3D = _pointArr[ i ];
Tweener.addTween( t, { y : 600 * Math.random(), time:0.4, delay:0.3 } );
Tweener.addTween( t, { y : -600 * Math.random(), time:0.8, delay:1 } );
Tweener.addTween( t, { y : 300*Math.random()+200, time:1.5 , delay:2 } );
}
}
_cnt++;
if (_cnt < NUM+1) {
Tweener.addTween( t, { time:0.2, onComplete:function(){ clickHandler(null) }} );
} else {
_cnt = 0;
}
}
private function enterframeHandler(e:Event):void
{
var angleY:Number = 0.001;
for(var i:int = 0; i<_pointArr.length; i++){
_pointArr[i].rotateY(angleY);
}
_triangleArr.sortOn("depth", Array.DESCENDING | Array.NUMERIC );
graphics.clear();
for (i = 0; i < _triangleArr.length; i++) {
if (_triangleArr[i].depth > -NUM*WID/2 ) {
_triangleArr[i].draw(graphics);
}
}
}
}
}
class Point3D
{
public var fl:Number = 250;
private var vpX:Number = 0;
private var vpY:Number = 0;
private var cX:Number = 0;
private var cY:Number = 0;
private var cZ:Number = 0;
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public function Point3D(x:Number=0, y:Number=0, z:Number=0)
{
this.x = x;
this.y = y;
this.z = z;
}
public function setVanishingPoint(vpX:Number, vpY:Number):void
{
this.vpX = vpX;
this.vpY = vpY;
}
public function setCenter(cX:Number, cY:Number, cZ:Number=0):void
{
this.cX = cX;
this.cY = cY;
this.cZ = cZ;
}
public function get screenX():Number
{
var scale:Number = fl / (fl + z + cZ);
return vpX + (cX + x) * scale;
}
public function get screenY():Number
{
var scale:Number = fl / (fl + z + cZ);
return vpY + (cY + y) * scale;
}
public function rotateX(angleX:Number):void
{
var cosX:Number = Math.cos(angleX);
var sinX:Number = Math.sin(angleX);
var y1:Number = y * cosX - z * sinX;
var z1:Number = z * cosX + y * sinX;
y = y1;
z = z1;
}
public function rotateY(angleY:Number):void
{
var cosY:Number = Math.cos(angleY);
var sinY:Number = Math.sin(angleY);
var x1:Number = x * cosY - z * sinY;
var z1:Number = z * cosY + x * sinY;
x = x1;
z = z1;
}
public function rotateZ(angleZ:Number):void
{
var cosZ:Number = Math.cos(angleZ);
var sinZ:Number = Math.sin(angleZ);
var x1:Number = x * cosZ - y * sinZ;
var y1:Number = y * cosZ + x * sinZ;
x = x1;
y = y1;
}
}
class Light
{
public var x:Number;
public var y:Number;
public var z:Number;
private var _brightness:Number;
public function Light(x:Number = -100, y:Number = -100, z:Number = -100, brightness:Number = 1)
{
this.x = x;
this.y = y;
this.z = z;
this.brightness = brightness;
}
public function set brightness(b:Number):void
{
_brightness = Math.max(b, 0);
_brightness = Math.min(_brightness, 1);
}
public function get brightness():Number
{
return _brightness;
}
}
import flash.display.Graphics;
class Triangle
{
private var pointA:Point3D;
private var pointB:Point3D;
private var pointC:Point3D;
private var color:uint;
public var light:Light;
public function Triangle(a:Point3D, b:Point3D, c:Point3D, color:uint=0xFF0000 )
{
pointA = a;
pointB = b;
pointC = c;
this.color = color;
}
public function draw(g:Graphics):void
{
if (isBackFace()) return
g.beginFill(getAdjustedColor());
// g.lineStyle(1, 0xFFFFFF);
g.moveTo(pointA.screenX, pointA.screenY);
g.lineTo(pointB.screenX, pointB.screenY);
g.lineTo(pointC.screenX, pointC.screenY);
g.lineTo(pointA.screenX, pointA.screenY);
g.endFill();
}
private function getAdjustedColor():uint
{
var red:Number = color >> 16;
var green:Number = color >> 8 & 0xff;
var blue:Number =color & 0xff;
var lightFactor:Number = getLightFactor();
red *= lightFactor;
green *= lightFactor;
blue *= lightFactor;
return red << 16 | green << 8 | blue;
}
private function getLightFactor():Number
{
var ab:Object = new Object();
ab.x = pointA.x - pointB.x;
ab.y = pointA.y - pointB.y;
ab.z = pointA.z - pointB.z;
var bc:Object = new Object();
bc.x = pointB.x - pointC.x;
bc.y = pointB.y - pointC.y;
bc.z = pointB.z - pointC.z;
var norm:Object = new Object();
norm.x = (ab.y * bc.z) - (ab.z * bc.y);
norm.y = -((ab.x * bc.z) - (ab.z * bc.x));
norm.z = (ab.x * bc.y) - (ab.y * bc.x);
var dotProd:Number = norm.x * light.x +
norm.y * light.y +
norm.z * light.z;
var normMag:Number = Math.sqrt(norm.x * norm.x +
norm.y * norm.y +
norm.z * norm.z);
var lightMag:Number = Math.sqrt(light.x * light.x +
light.y * light.y +
light.z * light.z);
return (Math.acos(dotProd / (normMag * lightMag)) / Math.PI)
* light.brightness;
}
private function isBackFace():Boolean
{
var cax:Number = pointC.screenX - pointA.screenX;
var cay:Number = pointC.screenY - pointA.screenY;
var bcx:Number = pointB.screenX - pointC.screenX;
var bcy:Number = pointB.screenY - pointC.screenY;
return cax * bcy > cay * bcx;
}
public function get depth():Number
{
var zpos:Number = Math.min(pointA.z, pointB.z );
zpos = Math.min(zpos, pointC.z);
return zpos;
}
}