はずむ粒
3Dの練習
*衝突判定の練習
*
*
* ドラッグでカメラ回転できます
* drag: camera rotates
*
*
* 参考にしたもの
* 実例で学ぶゲーム3D数学(http://www.oreilly.co.jp/books/9784873113777/)
* ActionScriptによるWebの3Dグラフィックス再入門 (2) - シェーディングでもっと3Dらしく
* (http://codezine.jp/article/detail/2235)
/**
* Copyright nackpan ( http://wonderfl.net/user/nackpan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/kwAZ
*/
/*
*3Dの練習
*衝突判定の練習
*
*
* ドラッグでカメラ回転できます
* drag: camera rotates
*
*
* 参考にしたもの
* 実例で学ぶゲーム3D数学(http://www.oreilly.co.jp/books/9784873113777/)
* ActionScriptによるWebの3Dグラフィックス再入門 (2) - シェーディングでもっと3Dらしく
* (http://codezine.jp/article/detail/2235)
*/
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
//import com.bit101.components.FPSMeter;
import flash.geom.Utils3D;
import flash.display.TriangleCulling;
/**
* ...
* @author nackpan
*
*/
[SWF(width="465", height="465", frameRate="30")]
public class Main extends Sprite
{
private var layer:Sprite;
private var layer2:Sprite;
private var layer3:Sprite;
private var hill:Hill;
private var count:int = 0;
private var intersection:Vector3D;
private var verticesLength:int;
///UV
private var uvVerticesBig:Vector.<Number>;
private var uvVertices:Vector.<Number>;
//床三角形の法線配列
private var triNormals:Vector.<Vector3D>;
//地面用bitmapData
private var floorBmpData:BitmapData;
private var baseBmpData:BitmapData;
private var bmpDataWidth:Number;
private var bmpDataHeight:Number;
//球の影用
private var ballShadow:BitmapData;
private var shadowRect:Rectangle;
private var shadowDestPoint:Point;
//三角形の配列
private var triangles:Vector.<Triangle>;
//
private var gravity:Number = 12;// 6;
private var hill_w:Number;
private var hill_h:Number;
private var hill_offset_x:Number;
private var hill_offset_z:Number;
private var hRows:int;
private var hCols:int;
private var hMax:int;
//ドラッグ関連
private var drag_x:Number;
private var dragDegree:Number = 0;
private var dragFlag:Boolean = false;
private var exMouseX:Number = 0;
//複数の球
private var spheres:Vector.<Sphere>;
private var sphereMax:int = 6;// 0;
//private var _fpsMeter:FPSMeter;
private var clipVal:Number = 4500;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
//_fpsMeter = new FPSMeter(this, 5, 0, "FPS: ");
layer = new Sprite();
addChild(layer);
layer.x = stage.stageWidth / 2;
layer.y = stage.stageHeight / 2 ;
layer2 = new Sprite();
addChild(layer2);
layer2.x = stage.stageWidth / 2;
layer2.y = stage.stageHeight / 2;
layer3 = new Sprite();
addChild(layer3);
layer3.x = stage.stageWidth / 2;
layer3.y = stage.stageHeight / 2;
//
//plane = new Plane();
//plane.init(0, 0, 0, 0, 0, 0, 0);
//plane.makeTriangles(400, 400);
//makeHill();
var hill_rows:int = 6;
var hill_cols:int = 6;
hill = new Hill();
hill.init(0, 0, 0, 0, 0, 0, 0);
hill.makeTriangles(hill_rows, hill_cols, 1000, 1000);
//地面の大きさ
hRows = hill_rows - 1;
hCols = hill_cols - 1;
hill_w = (hill_rows -1) * 1000;
hill_h = (hill_cols - 1 ) * 1000;
hill_offset_x = hill_w / 2;
hill_offset_z = hill_h / 2;
hMax = hRows * hCols;
hill.pos.y = -1800;
hill.pos.x = -(hill_w/2);
hill.pos.z = hill_h/2;
hill.updatePos();
hill.setLocalToGlobal();// ();
////地面用のUV//////////////////////////////////////////////////////
//hillのverticesから、xz平面に投影した頂点配列を作る(x,y,z)->(x,z)にする
uvVertices = new Vector.<Number>();
uvVertices = makeUV(hill.vertices);
//三角形の法線配列
triNormals = new Vector.<Vector3D>();
triNormals = makeNormals(hill.vertices, hill.indices);
//地面用bitmapData
bmpDataWidth = 400;
bmpDataHeight = 400;
floorBmpData = new BitmapData(bmpDataWidth, bmpDataHeight, false, 0xFF0000FF);
//元のポリゴンからUVに変換するにあたっての量
//移動量
var tx:Number = 0;// ((hill_rows - 1) * 1000 / 2);
var ty:Number = 0;//((hill_cols - 1) * 1000 / 2 );
//スケール
var sx:Number = bmpDataWidth / hill_w;
var sy:Number = bmpDataHeight / hill_h;
var m:Matrix = new Matrix(sx, 0, 0, sy, tx, ty);
//UV用を作る前に左上を(0,0)にした配列を作る
var i:int;
//bitmapDataに合わせる (400 と 4000)
for (i = 0; i < uvVertices.length; i+=2) {
uvVertices[i] = (uvVertices[i] + tx) * (bmpDataWidth / ((hill_rows-1)*1000));
uvVertices[i + 1] = (-uvVertices[i + 1] + ty ) * (bmpDataHeight/((hill_cols-1)*1000));
}
var light:Vector3D = new Vector3D(0.6, 1, 0, 0);
light.normalize();
paintTriangles(floorBmpData, uvVertices, hill.indices, triNormals, light, 1, 1);// sx, sy);
uvVerticesBig = new Vector.<Number>();
var tc:int = 0;
for (i = 0; i < uvVertices.length; i+=2) {
uvVertices[i] = (uvVertices[i] ) / bmpDataWidth;
uvVertices[i + 1] = (uvVertices[i + 1] ) / bmpDataHeight;
uvVerticesBig[tc++] = uvVertices[i];
uvVerticesBig[tc++] = uvVertices[i + 1];
uvVerticesBig[tc++] = 0;
}
baseBmpData = floorBmpData.clone();
//////三角形の配列をつくる
hill.setLocalToGlobal();
triangles = new Vector.<Triangle>();
makeTriangleVector(hill.vertices_g, hill.indices, triangles);
///////////////
//var colors:Array = [0xFF4422, 0xDD0000, 0x886600, 0xFF6600, 0xFF2200, 0xFF0000];
var colors:Array = [0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0x00FFFF];
spheres = new Vector.<Sphere>(2);
for (i = 0; i < sphereMax; i++){
var sphere:Sphere = new Sphere();
sphere.status = "move";
sphere.count = 0;
sphere.color = colors[i];
sphere.init(0, 0, 0, 0, 0, 0, 0);
sphere.makeTriangles(60);
//sphere.pos.y = 300;// -200;
//sphere.pos.z = 1300;// -600;
//sphere.pos.x = -400;// 20;
sphere.pos.y = Math.round(Math.random() * 100) + i * 250;
sphere.pos.x = -2400 + Math.round(Math.random() * 4800);
sphere.pos.z = -500 + Math.round(Math.random() * 1000);
sphere.updatePos();
spheres[i] = sphere;
}
//影の作成
ballShadow = makeBallShadow(spheres[0].radius, (hill_rows - 1) * 1000, (hill_cols - 1) * 1000, bmpDataWidth, bmpDataHeight);
shadowRect = new Rectangle(0, 0, 16, 16);
shadowDestPoint = new Point(100, 100);
//交点用
intersection = new Vector3D();
stage.addEventListener(MouseEvent.MOUSE_DOWN, dragDown);
//stage.addEventListener(MouseEvent.MOUSE_MOVE, dragMove);
stage.addEventListener(MouseEvent.MOUSE_UP, dragUp);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
////ドラッグ
private function dragDown(event:MouseEvent):void
{
drag_x = 0;
dragFlag = true;
exMouseX = mouseX;
stage.addEventListener(MouseEvent.MOUSE_MOVE, dragMove);
}
private function dragMove(event:MouseEvent):void
{
if(dragFlag){
drag_x = mouseX - exMouseX;
exMouseX = mouseX;
dragDegree += drag_x * .2;
if (dragDegree < -90) dragDegree = -90;
if (dragDegree > 90) dragDegree = 90;
}
}
private function dragUp(event:MouseEvent):void
{
dragFlag = false;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragMove);
}
//影を作る
private function makeBallShadow(r:Number, floor_w:Number, floor_h:Number, bitmap_w:Number, bitmap_h:Number):BitmapData
{
var sp:Shape = new Shape();
with (sp) {
graphics.clear();
graphics.beginFill(0x000000, .2);
graphics.drawCircle(r, r, r);
graphics.endFill();
}
var scale:Number = bitmap_w / floor_w;
var w:int = int(r * (bitmap_w / floor_w) * 2);
var h:int = int(r * (bitmap_h / floor_h) * 2);
var m:Matrix = new Matrix(scale, 0, 0, scale, 0, 0);
var bmpData:BitmapData = new BitmapData(w+1, h+1, true, 0);
bmpData.draw(sp, m);
return bmpData;
}
/////頂点配列、インデックス配列から三角形の配列を作る(頂点、法線、ax+by+cz+d=0表現でのd、ABベクトル、ACベクトルをもつ)
private function makeTriangleVector(vertices:Vector.<Number>, indices:Vector.<int>, triangles:Vector.<Triangle>):void
{
var num:int = 0;
//triangles = new Vector.<Triangle>();
var indicesLength:int = indices.length;
for (var i:int = 0; i < indicesLength ; i+=3){
var vnum:int = i;
//平面を求める
var n1:int = indices[vnum];
var n2:int = indices[vnum+1];
var n3:int = indices[vnum+2];
var p0_x:Number = vertices[n1*3];
var p0_y:Number = vertices[n1*3+1];
var p0_z:Number = vertices[n1*3+2];
var p1_x:Number = vertices[n2*3];
var p1_y:Number = vertices[n2*3+1];
var p1_z:Number = vertices[n2*3+2];
var p2_x:Number = vertices[n3*3];
var p2_y:Number = vertices[n3*3+1];
var p2_z:Number = vertices[n3 * 3 + 2];
var p0:Vector3D = new Vector3D(p0_x, p0_y, p0_z, 0);
var p1:Vector3D = new Vector3D(p1_x, p1_y, p1_z, 0);
var p2:Vector3D = new Vector3D(p2_x, p2_y, p2_z, 0);
var tri:Triangle = new Triangle(p0, p1, p2);
tri.num = num;
num++;
triangles.push(tri);
}
}
private function onEnterFrame(event:Event):void
{
var i:int;
var sp_n:int;
//重力分を加える
for (sp_n = 0; sp_n < sphereMax; sp_n++) {
var sphere:Sphere = spheres[sp_n];
if(sphere.status == "move"){
sphere.v.y += -gravity;
//速度を加える
sphere.updateVtoPos();
//位置を更新
sphere.updatePos();
}else {
sphere.count++;
if (sphere.count > 10) {
sphere.status = "move";
}
}
/////衝突判定
//注・すり抜けがひどいので直すこと
//注・球同士の衝突を作ること
////ボールがどの位置にいるか
var posA:Vector3D = sphere.pos;
var posB:Vector3D = posA.add(sphere.v);
var topleft_x:Number;// = posA.x - sphere.radius;
var topleft_z:Number;// = posA.z - sphere.radius;
var bottomRight_x:Number;
var bottomRight_z:Number;
if (posA.x <= posB.x) {
if (posA.z >= posB.z) {
topleft_x = posA.x;
topleft_z = posA.z;
bottomRight_x = posB.x;
bottomRight_z = posB.z;
}else {
topleft_x = posA.x;
topleft_z = posB.z;
bottomRight_x = posB.x;
bottomRight_z = posA.z;
}
}else {
if (posA.z >= posB.z) {
topleft_x = posB.x;
topleft_z = posA.z;
bottomRight_x = posA.x;
bottomRight_z = posB.z;
}else {
topleft_x = posB.x;
topleft_z = posB.z;
bottomRight_x = posA.x;
bottomRight_z = posA.z;
}
}
var bool:Boolean = false;
var offset_x:Number = hill_w/2;
var offset_z:Number = hill_h/2;
//左上座標から所属しているセル番号を算出する(
var cellNum:int = Math.floor((topleft_x + offset_x) / 1000)
+ Math.floor((hill_h - (topleft_z + offset_z)) / 1000) * hCols;
var cellNum2:int = Math.floor((bottomRight_x + offset_x) / 1000)
+ Math.floor((hill_h - (bottomRight_z + offset_z)) / 1000) * hCols;
//セルが範囲外なら、衝突判定はしない
if ((cellNum < 0 || cellNum >= hMax) && (cellNum2 < 0 || cellNum2 >= hMax)) {
}else {
//セルが範囲外なら、その分は、相手と同じセルということにしておく
if (cellNum < 0 || cellNum >= hMax) cellNum = cellNum2;
if (cellNum2 < 0 || cellNum2 >= hMax) cellNum2 = cellNum;
//このセルとその右と下と右下のセルが衝突候補になる
var collisionTriNumbers:Vector.<int> = new Vector.<int>();
checkTri(cellNum, cellNum2, collisionTriNumbers);
var len:int = collisionTriNumbers.length;
var normal:Vector3D = new Vector3D();
for (i = 0; i < len ; i++) {
//三角形で指定
var num:int = collisionTriNumbers[i];
bool = checkCollision(triangles[num], sphere, intersection);
if (bool) {
normal = triangles[num].n.clone();
break;
}
}
}
if (bool) {
collisionResolution(normal, intersection, sphere);
}
//////球と地面との前後関係処理
////球がどの領域にいるか調べる
var vec_x:Number = 0;
var vec_z:Number = 0;
if (sphere.pos.z > 2500) {
if (sphere.pos.x > 2500) {
//2
vec_x = 1/1.414;
vec_z = 1/1.414;
}else if (sphere.pos.x < -2500) {
//0
vec_x = -1/1.414;
vec_z = 1/1.414;
}else {
//1
vec_x = 0;
vec_z = 1;
}
}else if (sphere.pos.z < -2500) {
if (sphere.pos.x > 2500) {
//8
vec_x = 1/1.414;
vec_z = -1/1.414;
}else if (sphere.pos.x < -2500) {
//6
vec_x = -1/1.414;
vec_z = -1/1.414;
}else {
//7
vec_x = 0;
vec_z = -1;
}
}else {
if (sphere.pos.x > 2500) {
//5
vec_x = 1;
vec_z = 0;
}else if (sphere.pos.x < -2500) {
//3
vec_x = -1;
vec_z = 0;
}else {
//4
vec_x = 0;
vec_z = 0;
}
}
var radian:Number = -dragDegree / 180 * Math.PI;
var sisenVec_x:Number = Math.sin(radian);
var sisenVec_z:Number = -Math.cos(radian);
var dotProduct:Number = vec_x * sisenVec_x + vec_z * sisenVec_z;
if (vec_x == 0 && vec_z == 0) {
dotProduct = 1; //地面の上にある球は手前に表示 (応急処置)
}
if (dotProduct > 0.4) {
//手前
sphere.layer = layer3;
}else{
sphere.layer = layer;
}
if (sphere.pos.y <= -6420 || sphere.pos.z <= -clipVal
|| sphere.pos.z >= clipVal || sphere.pos.x <= -clipVal || sphere.pos.x >= clipVal) {
sphere.pos.y = -600 + Math.round(Math.random() * 400);
sphere.pos.x = -2400 + Math.round(Math.random() * 4800);
sphere.pos.z = -2400 + Math.round(Math.random() * 4000);
sphere.v.x = 0;
sphere.v.y = 0;
sphere.v.z = 0;
sphere.updatePos();
sphere.status = "wait";
sphere.count = 0;
}
}
render();
count++;
}
//衝突候補を探す---5*5のマス目専用にしたままになっている
private function checkTri(cellNum:int, cellNum2:int, collisionTriangles:Vector.<int>):void
{
var rows:int = 5;
var cols:int = 5;
var lastRowsNum:int = cols * (rows-1) -1;
collisionTriangles.length = 0;
var cNum:int = cellNum * 2;
//cellNum++;
//最末尾ならここでおしまい。あるいは、左上座標と右下座標が同じセルを指していたなら、ここでおしまい
if (cellNum >= hMax-1 || cellNum == cellNum2) {
collisionTriangles.push(cNum, cNum + 1);
return;
}
//最下端なら左右2つでおしまい
if (cellNum >= lastRowsNum) {
collisionTriangles.push(cNum, cNum + 1, cNum+2, cNum + 3);
return;
}
//右端なら上下2つでおしまい
var mod:int = cellNum % 5;
if (mod == 4) {
collisionTriangles.push(cNum, cNum + 1, cNum + 10, cNum + 11);
return;
}
//cellNumのセル、その右と下と右下をチェック
collisionTriangles.push(cNum, cNum + 1, cNum + 2, cNum + 3,
cNum + 10, cNum + 11, cNum + 12, cNum + 13);
return;
}
//////////////////衝突判定部分///////////////////////////////////////
private function checkCollision(tri:Triangle, sphere:Sphere, cp:Vector3D):Boolean
{
////三角形で指定
var p0_x:Number = tri.p0.x;
var p0_y:Number = tri.p0.y;
var p0_z:Number = tri.p0.z;
var p1_x:Number = tri.p1.x;
var p1_y:Number = tri.p1.y;
var p1_z:Number = tri.p1.z;
var p2_x:Number = tri.p2.x;
var p2_y:Number = tri.p2.y;
var p2_z:Number = tri.p2.z;
var n:Vector3D = tri.n.clone();// e1.crossProduct(e3);
var d:Number = n.x * p0_x + n.y * p0_y + n.z * p0_z;
//ボールの中心点と平面との距離を調べる
var pos:Vector3D = sphere.pos;
var q:Vector3D = pos;
var distance:Number = q.dotProduct(n) - d;
//移動後のボールの中心点と平面との距離を調べる
var q2:Vector3D = pos.add(sphere.v);
var distance2:Number = q2.dotProduct(n) - d;
var sphere_r:Number = sphere.radius;
//ボールが平面と交差しているなら
if (distance <= sphere.radius && distance >= -sphere.radius) {
var dn:Vector3D = new Vector3D;
dn = n.clone();
dn.scaleBy(distance);
var intersection1:Vector3D = q.subtract(dn);
//交点が三角形の内側にあるかどうか調べる
var boolq:Boolean = innerTriangle(n, intersection1, tri.p0, tri.p1, tri.p2, tri.num);// pp0, pp1, pp2, tri.num);
if (boolq) {
//ボールの底の部分の軌跡と、平面がどこで交差しているか調べる
var denom1:Number = n.dotProduct(sphere.v);//distance + d
var t1:Number = (sphere_r - distance) / denom1;
//sphere.v.y += -gravity
//intersection = intersection1.clone();
cp.x = intersection1.x;
cp.y = intersection1.y;
cp.z = intersection1.z;
return true;
}
return false;
}
//ボールの軌跡が平面と交差しているなら
if (distance >= 0 && distance2 <= sphere_r) {
//ボールの底の部分の軌跡と、平面がどこで交差しているか調べる
var denom:Number = n.dotProduct(sphere.v);//distance + d
var t:Number = (sphere_r - distance) / denom;
//交点を調べる
var rn:Vector3D = n.clone();
rn.scaleBy(sphere_r);
var tv:Vector3D = sphere.v.clone();
tv.scaleBy(t);
var intersection2:Vector3D = pos.add(tv);
intersection2.decrementBy(rn);
//intersection = intersection2.clone();
cp.x = intersection2.x;
cp.y = intersection2.y;
cp.z = intersection2.z;
//交点が三角形の内側にあるかどうか調べる
//var p0:Vector3D = new Vector3D(p0_x, p0_y, p0_z, 0);
//var p1:Vector3D = new Vector3D(p1_x, p1_y, p1_z, 0);
//var p2:Vector3D = new Vector3D(p2_x, p2_y, p2_z, 0);
var bool:Boolean = innerTriangle(n, intersection2, tri.p0, tri.p1, tri.p2, tri.num);// p0, p1, p2, tri.num);
if (bool) {
//ボールが平面に到達したとして、その分のgravityを速度に足す
//(そうしないと、跳ね返り後のボールの速度がすごく落ちてしまった)
sphere.v.y += -gravity ;// * t;
return true;
}
return bool;
}
return false;
}
private function innerTriangle(n:Vector3D, p:Vector3D, p0:Vector3D, p1:Vector3D, p2:Vector3D, num:int):Boolean
{
//どの平面に投影するかを決定し、uとvを計算する
var u0:Number, u1:Number, u2:Number;
var v0:Number, v1:Number, v2:Number;
var nx:Number = Math.abs(n.x);
var ny:Number = Math.abs(n.y);
var nz:Number = Math.abs(n.z);
if(nx > ny){
if (nx > nz) {
//yz平面へ投影
//x情報捨てる
u0 = p.y - p0.y;
u1 = p1.y - p0.y;
u2 = p2.y - p0.y;
v0 = p.z - p0.z;
v1 = p1.z - p0.z;
v2 = p2.z - p0.z;
}else{
u0 = p.x - p0.x;
u1 = p1.x - p0.x;
u2 = p2.x - p0.x;
v0 = p.y - p0.y;
v1 = p1.y - p0.y;
v2 = p2.y - p0.y;
}
}else{
if (ny > nz) {
//xz平面
u0 = p.x - p0.x;
u1 = p1.x - p0.x;
u2 = p2.x - p0.x;
v0 = p.z - p0.z;
v1 = p1.z - p0.z;
v2 = p2.z - p0.z;
}else{
u0 = p.x - p0.x;
u1 = p1.x - p0.x;
u2 = p2.x - p0.x;
v0 = p.y - p0.y;
v1 = p1.y - p0.y;
v2 = p2.y - p0.y;
}
}
//分母を計算し、無効かどうかをチェックする
var temp:Number = u1 * v2 - v1 * u2;
if(!(temp != 0.0)){
return false;
}
temp = 1.0 / temp;
//重心座標を計算し、各ステップで範囲外のチェックをする
var alpha:Number = (u0 * v2 - v0 * u2) * temp;
if(!(alpha >= 0.0)){
return false;
}
var beta:Number = (u1 * v0 - v1 * u0) * temp;
if(!(beta >= 0.0)){
return false;
}
var gamma:Number = 1.0 - alpha - beta;
if(!(gamma >= 0.0)){
return false;
}
return true;
}
private function collisionResolution(n:Vector3D, p:Vector3D, sphere:Sphere):void
{
var rn:Vector3D = n.clone();
var v:Vector3D = sphere.v;// new Vector3D();
var vn:Number = v.dotProduct(n);
n.scaleBy((1 + 1) * vn);
v.decrementBy(n);
sphere.v = v;
//位置補正
rn.scaleBy(sphere.radius);
var p2:Vector3D = p.clone();
p2.incrementBy(rn);
sphere.pos.x = p2.x;
sphere.pos.y = p2.y;
sphere.pos.z = p2.z;
sphere.updatePos();
var sx:int = int(sphere.pos.x);
var sy:int = int(sphere.pos.y);
var sz:int = int(sphere.pos.z);
var svx:int = int(sphere.v.x);
var svy:int = int(sphere.v.y);
var svz:int = int(sphere.v.z);
//しっぽにいれる
//insertTail(sphere.pos);
}
private function render():void
{
var i:int;
layer.graphics.clear();
layer2.graphics.clear();
layer3.graphics.clear();
//テクスチャ更新
floorBmpData.draw(baseBmpData);
for (i = 0; i < sphereMax; i++) {
var sphere:Sphere = spheres[i];
//ボールの影をつける
shadowDestPoint.x = Math.ceil( (sphere.pos.x-sphere.radius+hill_offset_x) * ( bmpDataWidth / hill_w));
shadowDestPoint.y = Math.ceil( (( - (sphere.pos.z+sphere.radius)+hill_offset_z)) * ( bmpDataHeight / hill_h) );
floorBmpData.copyPixels(ballShadow, shadowRect, shadowDestPoint, null, null, true);
//オブジェクトの頂点のグローバル座標を最新のものにする
sphere.setLocalToGlobal();
}
var m3D:Matrix3D = new Matrix3D();
//カメラ
//カメラの回転を戻す
m3D.appendRotation( -dragDegree , Vector3D.Y_AXIS);
//
//カメラの移動を戻す
m3D.appendTranslation(0, 0, 5600 );
var p:PerspectiveProjection = new PerspectiveProjection();
p.fieldOfView = 55;
var proj:Matrix3D = p.toMatrix3D();
m3D.append(proj);
var vec_m:Vector.<Number> = new Vector.<Number>();
vec_m.push(1, 0, 0, 0, 0 , -1, 0 , 0, 0, 0 , 1, 0, 0, 0, 0, 1);
var nm:Matrix3D = new Matrix3D(vec_m);
m3D.append(nm);
var vout:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
vout.length = 0;
//ビュー変換
Utils3D.projectVectors(m3D, hill.vertices_g, vout, uvVerticesBig);
draw2(layer2, vout, hill.indices, uvVerticesBig);
vout.length = 0;
//球の整列
var s:int = 0;
var vin:Vector.<Number> = new Vector.<Number>();
for (i = 0; i < sphereMax; i++) {
//ワールド座標
vin[s++] =spheres[i].pos.x;
vin[s++] =spheres[i].pos.y;
vin[s++] =spheres[i].pos.z;
}
m3D.transformVectors(vin, vout);
s = 0;
for (i = 0; i < sphereMax; i++) {
spheres[i].viewPos.x = vout[s++];
spheres[i].viewPos.y = vout[s++];
spheres[i].viewPos.z = vout[s++];
}
//zソート
spheres.sort(depthSort);
for (i = 0; i < sphereMax; i++) {
sphere = spheres[i];
//ビュー変換
Utils3D.projectVectors(m3D, sphere.vertices_g, vout, uvts);
draw(sphere.layer, vout, sphere.indices, uvts, sphere.color);
}
}
private function draw(sprite:Sprite, vertices:Vector.<Number>, indices:Vector.<int>, uvtData:Vector.<Number>, color:uint):void
{
sprite.graphics.lineStyle(0, 0xAA0000, .5);
//sprite.graphics.drawCircle(0, 0, 50);
sprite.graphics.beginFill(color);
sprite.graphics.drawTriangles(vertices, indices);// , uvtData, TriangleCulling.NEGATIVE);
sprite.graphics.endFill();
}
private function draw2(sprite:Sprite, vertices:Vector.<Number>, indices:Vector.<int>, uvtData:Vector.<Number>):void
{
sprite.graphics.lineStyle(0,0, .4);
sprite.graphics.beginBitmapFill(floorBmpData);// 0xFF0000);
sprite.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE);
sprite.graphics.endFill();
}
//シェーディング--n:面の法線、l:光源の方向
public function flatShading(n:Vector3D, l:Vector3D):uint {
var brightness:Number = n.dotProduct(l);
if (brightness <= 0) {
return 0xFFFFFFFF;
}
var r:uint = 255;
var g:uint = 255;
var b:uint = 255;
// 0xRRGGBB形式にする
var color:uint = (uint(r * brightness) << 16) | (uint(g * brightness) << 8) | uint(b * brightness);
return color;
}
//三角形複数を塗る vertices:x,y,x,y,...三角形の頂点が入っている
private function paintTriangles(bmpData:BitmapData, vertices:Vector.<Number>, indices:Vector.<int>,
normals:Vector.<Vector3D>, l:Vector3D, scale_w:Number, scale_h:Number):void
{
//三角形の数だけループ
var len:int = normals.length;
for (var i:int = 0; i < len; i++) {
var num0:int = indices[i * 3];
var num1:int = indices[i * 3 + 1];
var num2:int = indices[i * 3 + 2];
var p0x:int = vertices[num0 * 2];
var p0y:int = vertices[num0 * 2 + 1];
var p1x:int = vertices[num1 * 2];
var p1y:int = vertices[num1 * 2 + 1];
var p2x:int = vertices[num2 * 2];
var p2y:int = vertices[num2 * 2 + 1];
//光源の方向と面の法線から塗る色を決める
//l.dotProduct(normals[i])
var color:uint = flatShading(normals[i], l);
var sp:Shape = new Shape();
with (sp.graphics) {
clear();
beginFill(color);
moveTo(p0x, p0y);
lineTo(p1x, p1y);
lineTo(p2x, p2y);
lineTo(p0x, p0y);
endFill();
}
////色を塗った三角形をbitmapDataの所定の位置に貼る
var m:Matrix = new Matrix();
//m.tx = p0x * scale_w;
//m.ty = p0y * scale_h;
m.scale(scale_w, scale_h);
bmpData.draw(sp, m);
}
}
private function makeUV(vertices:Vector.<Number>):Vector.<Number>
{
var rtn:Vector.<Number> = new Vector.<Number>();
var len:int = vertices.length;
for (var i:int = 0; i < len; i+=3) {
var x:Number = vertices[i];
var z:Number = vertices[i + 2];
rtn.push(x, z);
}
return rtn;
}
//
private function makeNormals(vertices:Vector.<Number>, indices:Vector.<int>):Vector.<Vector3D>
{
var rtn:Vector.<Vector3D> = new Vector.<Vector3D>();
var indicesLength:int = indices.length;
for (var i:int = 0; i < indicesLength ; i+=3){
var vnum:int = i;
//平面を求める
var n1:int = hill.indices[vnum];
var n2:int = hill.indices[vnum+1];
var n3:int = hill.indices[vnum+2];
var p1_x:Number = hill.vertices[n1*3];
var p1_y:Number = hill.vertices[n1*3+1];
var p1_z:Number = hill.vertices[n1*3+2];
var p2_x:Number = hill.vertices[n2*3];
var p2_y:Number = hill.vertices[n2*3+1];
var p2_z:Number = hill.vertices[n2*3+2];
var p3_x:Number = hill.vertices[n3*3];
var p3_y:Number = hill.vertices[n3*3+1];
var p3_z:Number = hill.vertices[n3 * 3 + 2];
var e3:Vector3D = new Vector3D(p2_x - p1_x, p2_y - p1_y, p2_z - p1_z, 0);
var e1:Vector3D = new Vector3D(p3_x - p2_x, p3_y - p2_y, p3_z - p2_z, 0);
var n:Vector3D = e1.crossProduct(e3);
n.normalize();
n.negate();
rtn.push(n);
}
return rtn;
}
private function depthSort(objA:Sphere, objB:Sphere):int
{
var posA:Vector3D = objA.viewPos;
var posB:Vector3D = objB.viewPos;
return posB.z - posA.z;
}
}
}
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.display.Shape;
class Object3D
{
public var sp:Shape;
public var pos:Vector3D;
public var viewPos:Vector3D;
public var v:Vector3D;
public var dir:Vector3D;
public var speed:Number;
public var n:int;
//public var z:Number;
//このオブジェクトの移動と回転を示す行列
public var matrix:Matrix3D;
//頂点配列、インデックス配列、uvt配列
public var vertices:Vector.<Number>;
public var indices:Vector.<int>;
public var uvts:Vector.<Number>;
public var vertices_g:Vector.<Number>;
public function Object3D():void
{
}
public function init(x:Number, y:Number, z:Number,
vx:Number, vy:Number, vz:Number,
n:int):void
{
pos = new Vector3D(x, y, z, 0);
v = new Vector3D(vx, vy, vz, 0);
//this.sp = sp;
this.n = n;
dir = new Vector3D(vx, vy, vz, 0);
speed = dir.length;
dir.normalize();
//z = z;
viewPos = new Vector3D();
vertices = new Vector.<Number>();
indices = new Vector.<int>();
uvts = new Vector.<Number>();
vertices_g = new Vector.<Number>();
matrix = new Matrix3D();
}
public function updateVtoPos():void
{
pos.x += v.x;
pos.y += v.y;
pos.z += v.z;
}
public function clone():Object3D
{
var obj:Object3D = new Object3D();
obj.init(this.pos.x, this.pos.y, this.pos.z, this.v.x, this.v.y, this.v.z, this.n);
return obj;
}
//頂点をローカル座標からワールド座標に変更
public function setLocalToGlobal():void
{
matrix.transformVectors(vertices, vertices_g);
}
public function updatePos():void
{
matrix.position = pos;// (pos.x, pos.y, pos.z);//= val;
}
}
class Triangle
{
public var p0:Vector3D;
public var p1:Vector3D;
public var p2:Vector3D;
public var n:Vector3D;
public var d:Number;
public var e01:Vector3D;
public var e02:Vector3D;
public var num:int;
//
public var offset_x:Number;
public var offset_y:Number;
public function Triangle(p0:Vector3D, p1:Vector3D, p2:Vector3D):void
{
this.p0 = p0.clone();
this.p1 = p1.clone();
this.p2 = p2.clone();
e01 = new Vector3D(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z, 0);
e02 = new Vector3D(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z, 0);
n = e02.crossProduct(e01);
n.normalize();
n.negate();
d = n.x * p1.x + n.y * p1.y + n.z * p1.z;
}
}
import flash.display.Sprite;
import flash.geom.Vector3D;
import flash.display.Shape;
class Sphere extends Object3D
{
public var radius:Number;
public var status:String;
public var count:int;
public var layer:Sprite;
public var color:uint;
public function Sphere():void
{
}
//頂点配列、インデックス配列、uvt配列
public function makeTriangles(radius:Number):void
{
this.radius = radius;
var cols:int = 8;
var rows:int = 8;
var offset:Number = 0;// -= .02;
vertices.length = 0;
uvts.length = 0;
for(var i:int = 0; i < rows; i++)
{
for(var j:int = 0; j < cols; j++)
{
var angle:Number = Math.PI * 2 / (cols - 1) * j;
var angle2:Number = Math.PI * i / (rows - 1) - Math.PI / 2;
var xpos:Number = Math.cos(angle + offset) * radius * Math.cos(angle2);
var ypos:Number = Math.sin(angle2) * radius;
var zpos:Number = Math.sin(angle + offset) * radius * Math.cos(angle2);
///var scale:Number = focalLength / (focalLength + zpos + centerZ);
///vertices.push(xpos * scale,
/// ypos * scale);
///uvts.push(j / (cols - 1), i / (rows - 1));
///uvts.push(scale);
vertices.push(xpos, ypos, zpos);
}
}
for(i = 0; i < rows; i++)
{
for(j = 0; j < cols; j++)
{
if(i < rows - 1 && j < cols - 1)
{
indices.push(i * cols + j,
i * cols + j + 1,
(i + 1) * cols + j);
indices.push(i * cols + j + 1,
(i + 1) * cols + j + 1,
(i + 1) * cols + j);
}
}
}
}
}
import flash.geom.Vector3D;
import flash.display.Shape;
class Hill extends Object3D
{
public function Hill():void
{
}
//頂点配列、インデックス配列、uvt配列
public function makeTriangles(rows:int, cols:int, cell_w:int, cell_h:int):void
{
var i:int, j:int;
//var rows:int = 10;
//var cols:int = 10;
//var cell_w:int = 100;
//var cell_h:int = 100;
//var offset_x:int = -cell_w * ((cols-1) / 2);
//var offset_z:int = cell_h * ((rows-1) / 2);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
var xpos:Number = j * cell_w ;// + offset_x ;
var ypos:Number;
//if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) {
//ypos = Math.round(Math.random() * 60 ) + 260;
//}else{
ypos = Math.round(Math.random() * 260 ) ;// (rows - i) * 30 + Math.round(Math.random() * 200 );
//}
var zpos:Number = - (i * cell_h) ;// + offset_z;
vertices.push(xpos, ypos, zpos);
if (i < rows - 1 && j < cols - 1)
{
//三角形
indices.push(i * cols + j,
i * cols + j + 1,
(i + 1) * cols + j);
indices.push(i * cols + j + 1,
(i + 1) * cols + j + 1,
(i + 1) * cols + j);
}
}
}
}
}