fc2ブログ

PV3D 地形データの自動生成(メッシュ版)

2008-03-19 | 20:37

以前作成した地形データの自動生成のメッシュ版
(地形データを自動生成するアルゴリズム自体は全く同じです)
マウスクリックで新しい地形データを生成し直します。
Click to generate new landform.


package
{
    import flash.display.*;
    import flash.events.*;

    import org.papervision3d.view.BasicView;
    import org.papervision3d.core.geom.TriangleMesh3D;
    import org.papervision3d.core.geom.renderables.Triangle3D;
    import org.papervision3d.core.geom.renderables.Vertex3D;
    import org.papervision3d.core.math.NumberUV;
    import org.papervision3d.lights.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.materials.special.*;
    import org.papervision3d.materials.shadematerials.* ;
    import org.papervision3d.materials.utils.*;

    [SWF(width=600, height=500, backgroundColor=0x111111)]

    public class Mesh extends BasicView
    {
        private var mesh:TriangleMesh3D=null;

        private var landForm:LandForm;
        private var pointLight:PointLight3D;

        private var f:Number = 0;

        public function Mesh()
        {
            stage.frameRate = 10;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;

            super (0,0,true,false,"CAMERA3D");
            init3D();

            stage.addEventListener(MouseEvent.MOUSE_DOWN, buildMesh);

            buildMesh();
        }

        public function init3D():void
        {
            camera.y = 100;
            camera.z = -300;
            camera.focus = 500;
            camera.zoom = 8;

            pointLight = new PointLight3D(true);
            pointLight.y = 300;

            startRendering();
        }

        override protected function onRenderTick(event:Event=null):void
        {
            f += 0.02;
            pointLight.x = 100*Math.cos(f);
            pointLight.z = 100*Math.cos(f);
            mesh.rotationY += 0.1;
            super.onRenderTick(event);
        }

        private function buildMesh(e:* = null):void
        {
            if(mesh)
                scene.removeChild(mesh);

            var mat:PhongMaterial = new PhongMaterial(pointLight, 0xcc5555, 0x111111, 1);
            mesh = new TriangleMesh3D( mat, [], [], null );
            scene.addChild(mesh);

            landForm = new LandForm(128);
            for(var i:int = 0 ; i < 6 ; i++)
                landForm.split();

            mesh.geometry.vertices = [];
            mesh.geometry.faces = [];

            for(var z:int = 0 ; z < landForm.length ; z++){
                var vertices:Array = [];
                for(var x:int = 0 ; x < landForm.length ; x++){
                    mesh.geometry.vertices.push(
                            new Vertex3D(landForm.scale*(x - ( landForm.length - 1)/2),
                                landForm.value(x,z),
                                landForm.scale*(z - (landForm.length - 1)/2)));
                }
            }

            for(z = 0 ; z < landForm.length - 1; z++){
               for(x = 0 ; x < landForm.length - 1; x++){
                var uvA:NumberUV = new NumberUV( 0, 0 );
                var uvC:NumberUV = new NumberUV( 1, 0 );
                var uvB:NumberUV = new NumberUV( 0, 1 );
                
                mesh.geometry.faces.push( new Triangle3D( mesh, 
                            new Array(mesh.geometry.vertices[x * landForm.length + z],
                                mesh.geometry.vertices[x * landForm.length + z + 1],
                                mesh.geometry.vertices[(x+1) * landForm.length + z]),
                            null, new Array(uvC,uvB,uvA) ));
                mesh.geometry.faces.push( new Triangle3D( mesh, 
                            new Array(mesh.geometry.vertices[x * landForm.length + z + 1],
                                mesh.geometry.vertices[(x+1) * landForm.length + z + 1],
                                mesh.geometry.vertices[(x+1) * landForm.length + z]),
                            null, new Array(uvA,uvB,uvC) ));
                   }
                }
            mesh.geometry.ready = true;

        }

    }
}
internal class LandForm{
    private var buffer:Array = new Array();

    private var range:int ;
    private var splitCnt:int = 0;
    private var base:Number = 10;

    // range は2の乗数であること
    public function LandForm(range:int){
        this.range = range;
        clear();
    }

    public function clear():void{
        buffer = [];
        // 変位が格納される2次元配列(w*h)
        for(var j:int = 0 ; j < range+1; j++){
            var aa:Array = new Array();
            for(var k:int = 0 ; k < range+1; k++)
                aa.push(0);
            buffer.push(aa);
        }
        buffer[0][0] = base*Math.random();
        buffer[range][0] = base*2*Math.random();
        buffer[0][range] = base*3*Math.random();
        buffer[range][range] = base*4*Math.random();
        //trace(buffer[128][128]);
    }

    public function split():void{
        if(Math.pow(2,splitCnt) >= range){
            splitCnt=0;
            clear();
        }
        splitCnt++;

        for(var x:int = 1 ; x < length ; x+=2){
            for(var y:int = 1 ; y < length ; y+=2){
                splitMidPoint(x, y);
            }
        }
    }

    private function splitMidPoint(x:int, y:int):void{

        midPoint(x,y,true);
        midPoint(x-1,y);
        midPoint(x+1,y);
        midPoint(x,y-1);
        midPoint(x,y+1);

    }
    private function midPoint(x:int, y:int, squere:Boolean=false):void{
        //trace(x.toString() + ":" + y.toString());

        var sum:Array = [];
        if(squere){
            if(x > 0 && y > 0 && value(x-1,y-1) != 0 )
                sum.push(value(x-1, y-1));
            if(x > 0 && y < length-1 && value(x-1,y+1) != 0)
                sum.push(value(x-1, y+1));
            if(x < length - 1 && y > 0 && value(x+1,y-1) != 0)
                sum.push(value(x+1, y-1));
            if(x < length - 1 && y < length - 1 && value(x+1,y+1) != 0)
                sum.push(value(x+1, y+1));
        }else{ // diamonds
            if(y > 0 && value(x,y-1) != 0)
                sum.push(value(x, y-1));
            if(x > 0 && value(x-1,y) != 0)
                sum.push(value(x-1, y));
            if(y < length - 1 && value(x,y+1) != 0)
                sum.push(value(x, y+1));
            if(x < length - 1 && value(x+1,y) != 0)
                sum.push(value(x+1, y));
        }

        var sumOfHeight:Number = 0;
        for each(var v:Number in sum)
            sumOfHeight += v;

        var avg:Number = sumOfHeight / sum.length ;
        setValue(x,y, newHeight(avg));

    }

    private function newHeight(avg:Number):Number{
        //    return avg + (Math.random()*(Math.pow(2, - 0.3*splitCnt)));
        return avg*(1 + (Math.random ()*2 - 1) * scale * 0.019);
    }

    public function get length():int{
        return Math.pow(2, splitCnt) + 1;
    }

    public function get scale():int{
        return range / Math.pow(2,splitCnt);
    }

    public function value(x:int, z:int):Number{
        return buffer[x*scale][z*scale];
    }

    private function setValue(x:int, z:int, value:Number):void{
        buffer[x*scale][z*scale] = value;
    }
}

スポンサーサイト