In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

はずむ粒

3Dの練習
*衝突判定の練習
*
*
* ドラッグでカメラ回転できます
* drag: camera rotates
*
*
* 参考にしたもの
* 実例で学ぶゲーム3D数学(http://www.oreilly.co.jp/books/9784873113777/)
* ActionScriptによるWebの3Dグラフィックス再入門 (2) - シェーディングでもっと3Dらしく
* (http://codezine.jp/article/detail/2235)
Get Adobe Flash player
by nackpan 30 Jul 2010

    Tags

    3D
    Embed
/**
 * 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);
                    
                }
            }
        }
        
        
    }
    
    
    
}