Home > 梅原 > 地球儀

地球儀

20090626
Category:梅原 /Tags:

地球儀地球儀をつくってみた(といってもずいぶん前のを調整しただけだけど)。
地図画像を読み込み、色を取得し対応するポリゴンの色としている。また、bump mapも使っているので、凸凹も表現している。

地図の画像

地図の画像


bump map

bump map


元画像は100*50の画像なので、頂点の位置をxとyに置き、bump mapの色の濃さでzとした。 bump mapを強調するために山の高さを大げさにした。

アフィン変換、透視変換をして、4つの頂点ごとに、linetoで四角ポリゴンを描き、color mapから取得した色で塗りつぶし。

計算頂点数5151(=101*51計算を単純化するために一周した同じ位置を二回計算している)。手前の半球(見えている部分)のみを描画すればいいので、(だいたい)球の中心から向こう側は描画していない。このため 実際の同時描画数は5割強程度。 2600強くらいの四角ポリゴンを描画している。

AS3ではあるが、FlashPlayer10の3D用の機能(Matrix3D,Vector3Dなど)は使っていない。

Get Adobe Flash player

地球をクリックするたびに、101*51=5151頂点版、201*101=20301頂点版、501*251=125751頂点版に切り替わる。さすがに125751頂点版は重い。

201*101=20301頂点版、501*251=125751頂点版の画像は以下
earthmap500

▼color mapとbump mapの元画像
http://planetpixelemporium.com/earth.html

▼Wonderfl
http://wonderfl.net/code/c4feabce5beceb22843162582094a104eb624669

▼ActionScript AS3(FP9)

/*
だいぶ前に作ったやつのwonderfl移植版

クリックするたびに、
101*51=5151頂点版
201*101=20301頂点版
501*251=125751頂点版
が切り替わる。

さすがに125751頂点版は重い。

*/
package {
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Graphics;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.utils.getTimer;
	import flash.system.Security;
	[SWF(width="465", height="465", backgroundColor="0x000000")]
	public class Main extends Sprite {
		private var bmp_data:BitmapData;
		private var spec_data:BitmapData;
		private var bitmapz_array:Array;
		private var colormap_array:Array;
		private var clr:uint;
		private var bmpw:uint;
		private var bmph:uint;
		private var Math3D:Math3DClass = new Math3DClass();
		private var loadFiles_array:Array;
		private var baseURL:String = "";
		private var MultiLoader:MultiLoaderClass = new MultiLoaderClass("http://mztm.heteml.jp/crossdomain.xml");
		private var shape:Shape = new Shape();
		private var text_field:TextField = new TextField();
		private var count:int = 0;
		function Main(){

			if(isMztmjp()){
				baseURL = "http://www.mztm.jp/wp/wp-content/uploads/2009/06/";
			}else if(stage.loaderInfo.url.substr(0,5) == "file:"){
			}else{
				baseURL = "http://mztm.heteml.jp/umhr/wonderfl/";
				Security.loadPolicyFile("http://mztm.heteml.jp/crossdomain.xml");
			}

			loadFiles_array = MultiLoader.setLoad([baseURL+"earthmap100.png",baseURL+"earthbump100.png",baseURL+"earthmap200.png",baseURL+"earthbump200.png",baseURL+"earthmap500.png",baseURL+"earthbump500.png"],onImgComp);
			stage.addEventListener(MouseEvent.CLICK,CLICK);
			stage.addChild(shape);
			stage.addChild(text_field);
		}
		private function isMztmjp():Boolean{
			var _str:String = stage.loaderInfo.url;
			return _str.indexOf("mztm.jp") > -1;
			//return (_str.substr(0,5) == "file:" || _str.indexOf("mztm.jp") > -1);
		}
		private function CLICK(e:MouseEvent):void{
			count ++;
			count %=3;
			onImgComp(count*2);
		}

		private function onImgComp(num:int = 0):void{
			var w:int = loadFiles_array[num].width;
			var h:int = loadFiles_array[num].height;

			bmp_data = new BitmapData(w,h);
			spec_data = new BitmapData(w,h);
			bmpw = w+1;
			bmph = h+1;

			bmp_data.draw(loadFiles_array[num]);
			spec_data.draw(loadFiles_array[num+1]);
			main();
        }
		private function main():void{
			colormap_array = new Array();
			bitmapz_array = new Array();
			for (var k:uint = 0; k < bmph; k++) {
				for (var j:uint = 0; j < bmpw; j++) {
					colormap_array.push(uint(bmp_data.getPixel(j,k)));
					bitmapz_array.push([Number(j-bmpw/2),Number(k-bmph/2),Number(-spec_data.getPixel(j,k)/200000000)]);
				}
			}
			bmp_data.dispose();
			spec_data.dispose();

			for (var i:uint = 0; i < bitmapz_array.length; i++) {
				var nnx:Number = -Math.PI*(bitmapz_array[i][1]/(bmph-1));
				var nny:Number = -Math.PI*(bitmapz_array[i][0]/((bmpw-1)/2));
				var nx:Number = Math.cos(nnx)*Math.cos(nny);
				var ny:Number = Math.cos(nnx)*Math.sin(nny);
				var nz:Number = Math.sin(nnx);
				bitmapz_array[i][uint(0)] = Number(nx*200*(1-bitmapz_array[i][uint(2)]));
				bitmapz_array[i][uint(1)] = Number(ny*200*(1-bitmapz_array[i][uint(2)]));
				bitmapz_array[i][uint(2)] = Number(nz*200*(1-bitmapz_array[i][uint(2)]));
			}

function fn_calc(arg_bitmapz_array:Array,arg_view_array:Array,arg_bmpw:uint,arg_bmph:uint):void {
	Math3D.affine(arg_bitmapz_array,arg_view_array);
	for (var k:uint = 0; k < arg_bitmapz_array.length; k++) {
		arg_bitmapz_array[k] = Math3D.pertrans(arg_bitmapz_array[k]);
	}
	//render
	shape.graphics.clear();
	var g:Graphics = shape.graphics;
	var polygoncounter:uint
	for (var i:uint = 0; i < arg_bmph-1; i++) {
		var i1w:int = int((i+1)*arg_bmpw);
		var iw:int = int(i*arg_bmpw);
		for (var j:uint = 0; j < arg_bmpw-1; j++) {
			if (arg_bitmapz_array[uint(j+i*arg_bmpw)][uint(2)] < int(-5)) {
				continue;
			}
			var j1:int = int(j+1);
			polygoncounter++;
			g.beginFill(colormap_array[uint(j+iw)],uint(1));
			g.moveTo(arg_bitmapz_array[uint(j+iw)][uint(0)], arg_bitmapz_array[uint(j+iw)][uint(1)]);
			g.lineTo(arg_bitmapz_array[uint(j1+iw)][uint(0)], arg_bitmapz_array[uint(j1+iw)][uint(1)]);
			g.lineTo(arg_bitmapz_array[uint(j1+i1w)][uint(0)], arg_bitmapz_array[uint(j1+i1w)][uint(1)]);
			g.lineTo(arg_bitmapz_array[uint(j+i1w)][uint(0)], arg_bitmapz_array[uint(j+i1w)][uint(1)]);
		}
	}
	text_field.text = "計算頂点数:"+arg_bitmapz_array.length+"\n描画ポリゴン数:"+polygoncounter+"\nfps:"+fps;
}
//stage.addChild(shape);
shape.x = uint(stage.stageWidth/2);
shape.y = uint(stage.stageHeight/2);

//var text_field:TextField = new TextField();
text_field.textColor = 0xffffff;
text_field.wordWrap = true;
text_field.width = uint(200);
text_field.height = 60;
text_field.y = stage.stageHeight - text_field.height;
//stage.addChild(text_field);
/////
var draw_count:uint;          // 描画カウント
var fps:Number;          // 描画カウント
var old_timer:uint = getTimer();  // 時間待避
var aberage_array:Array = new Array();
if(!stage.hasEventListener(Event.ENTER_FRAME)){
	stage.addEventListener(Event.ENTER_FRAME ,ENTER_FRAME)
}
function ENTER_FRAME(e:Event):void{
	draw_count ++;         // 描画回数カウント
	// 1秒経過していれば
	if (getTimer()-old_timer >= 1000) {
		var aberage_array_length:int = aberage_array.push(draw_count);
		fps = 0;
		for (var m:uint = 0; m < aberage_array_length; m++) {
			fps += aberage_array[m];
		}
		fps /= aberage_array_length;
		if(aberage_array_length == 10){
			aberage_array.shift();
		}
		old_timer = getTimer();
		draw_count = 0;
	}
	view_array[uint(4)] += 0.05;
	fn_calc(Math3D.fn_copyarray(bitmapz_array),view_array,bmpw,bmph);

};

/////

var view_array:Array = new Array(0,0,0,Math.PI/2,0,0);
///

		}
	}
}

import flash.display.Sprite;
class Math3DClass extends Sprite {
	function Math3DClass(){
	}
	public function pertrans(arg_array:Array):Array {
		var _per:Number = 4000/(4000+arg_array[uint(2)]);
		return [arg_array[uint(0)] * _per,arg_array[uint(1)] * _per,arg_array[uint(2)]];
	}
	public function fn_copyarray(arg_array:Array):Array {
		var j:uint = new uint(arg_array.length);
		var _array:Array = new Array();
		for (var i:uint = 0; i < j; i++) {
			_array[int(i)] = [arg_array[int(i)][int(0)],arg_array[int(i)][int(1)],arg_array[int(i)][int(2)]];
		}
		return _array;
	}
	 //arrayの複製。再起処理を行っている。
	public function arrayClone(ar:Array):Array {
		var _array:Array = new Array();
		var _len:int = ar.length;
		for (var i:int = 0; i<_len; i++) {
			_array[i]=(ar[i] is Array)?arrayClone(ar[i]):ar[i];
		}
		return _array;
	}
	public function affine(data_array:Array,arg_array:Array):void {
		var n_cx:Number = Math.cos(arg_array[uint(3)]);
		var n_sx:Number = Math.sin(arg_array[uint(3)]);
		var n_cy:Number = Math.cos(arg_array[uint(4)]);
		var n_sy:Number = Math.sin(arg_array[uint(4)]);
		var n_cz:Number = Math.cos(arg_array[uint(5)]);
		var n_sz:Number = Math.sin(arg_array[uint(5)]);
		var d_x:Number = arg_array[uint(0)];
		var d_y:Number = arg_array[uint(1)];
		var d_z:Number = arg_array[uint(2)];
		var af_xx:Number = n_cz*n_cy+n_sx*n_sy*n_sz;
		var af_xy:Number = n_sx*n_sy*n_cz-n_sz*n_cy;
		var af_xz:Number = n_sy*n_cx;
		var af_yx:Number = n_cx*n_sz;
		var af_yy:Number = n_cx*n_cz;
		var af_yz:Number = -n_sx;
		var af_zx:Number = n_cy*n_sx*n_sz-n_sy*n_cz;
		var af_zy:Number = n_sy*n_sz+n_cy*n_sx*n_cz;
		var af_zz:Number = n_cx*n_cy;
		var dataarraylength_uint:uint = new uint(data_array.length);
		for (var j:uint = 0; j<dataarraylength_uint; j++) {
			var af_x:Number = data_array[uint(j)][uint(0)];
			var af_y:Number = data_array[uint(j)][uint(1)];
			var af_z:Number = data_array[uint(j)][uint(2)];
			data_array[uint(j)][uint(0)] = af_x*af_xx+af_y*af_xy+af_z*af_xz+d_x;
			data_array[uint(j)][uint(1)] = af_x*af_yx+af_y*af_yy+af_z*af_yz+d_y;
			data_array[uint(j)][uint(2)] = af_x*af_zx+af_y*af_zy+af_z*af_zz+d_z;
		}
	}
}

class MultiLoaderClass{
    import flash.system.Security;
    import flash.net.URLRequest;
    import flash.net.URLLoader;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.display.Loader;
    //import flash.display.LoaderInfo;

    private var onComplete:Function = function():void{};
    private var loadNum:int;
    private var loadCompNum:int;

    public function MultiLoaderClass(_str:String = null){
        if(_str != null){
            Security.loadPolicyFile(_str);
        }
    }

    public function setLoad(__item_array:Array,_onComp:Function = null):Array{
        loadCompNum = loadNum = 0;
        onComplete = _onComp;
        var _array:Array = new Array();
        var _length:int = __item_array.length;
        for (var i:int = 0; i < _length; i++) {
            if(__item_array[i] == null){continue};
            var _extension:String = __item_array[i].substr(-4,4).toLowerCase();//拡張子を取り出す。
            if(_extension == ".xml"){
                loadNum ++;
                _array[i] = fnURLLoader(__item_array[i]);
            }else if(_extension == ".jpg" || _extension == ".png"){
                loadNum ++;
                _array[i] = fnLoader(__item_array[i]);
            }else{
                //_array[i] = null;
            }
        }
        return _array;
    }
    private function fnURLLoader(__url:String):URLLoader{
        var _loader : URLLoader = new URLLoader();
        _loader.load(new URLRequest(__url));
        _loader.addEventListener (Event.COMPLETE,completeHandler);
        _loader.addEventListener (IOErrorEvent.IO_ERROR, ioErrorHandler);
        return _loader;
    }

    private function fnLoader(__url:String):Loader{
        var _loader:Loader = new Loader();
        _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
        _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        _loader.load(new URLRequest(__url));
        //_loader.name = __url;
        return _loader;
    }

    private function completeHandler(event:Event = null):void {
        loadCompNum ++;
        if(loadCompNum == loadNum){
            onComplete();
        }
        //var loaderInfo:LoaderInfo=event.currentTarget as LoaderInfo;
        //var loader:Loader=loaderInfo.loader;
        //addChild(loader);
    }

    private function ioErrorHandler(event:IOErrorEvent):void {
        //event.text = "Error #2035: URL が見つかりません。 URL: file:///~~~~~";
        //event.text = "Error #2036: 読み込みが未完了です。 URL: http://~~~~~";
        //から、URLのみを取り出す。
        //trace(String(event.text).substr(String(event.text).indexOf(" URL: ")+6),"*****");
        completeHandler();
    }
}

関連記事:

  1. No comments yet.
Comments are closed.