見出し画像

ティラノスクリプトの3D その4:スカイドーム

初めに

スカイドームと言っても、結局 [3d_sphere_new] で、パラメータを加減すれば代用できるという考え方もあります。しかし、普通の球とスカイドームでは異なる点があります。スカイドームは、基本シーンに一つで、適時テクスチャーを切り替えて使います。今の [3d_sphere_new] には、この機能はありません。また、[3d_sphere_new] で毎回細かいパラメータ設定をすると間違いも起こり安くなります。

実現法

スカイドームを three.js で作るのは、SphereGeometry を指定して geometry  を作り、MeshBasicMaterial のmaterial に テクスチャーイメージを割り当て、その二つをを指定した Mesh のObject3D を作ればできます。ただ、テクスチャーを切り替える為に、マテリアルのテクスチャーを新しいテクスチャーに張り付けなおすと、元のテクスチャーは消えてしまい、毎回再ロードすることになります。出来れば、ロードしたテクスチャーを何処かに保管しておいて、適時切り替えて使いたいものです。問題はどう保管するかということです。今の所2つの案を考えました。一つは、Object3Dというオブジェクトの中に userData というユーザが自由に使っていいオブジェクトがありますので、そこに名前を付けて保管するという方法です。もう一つは、各テクスチャー毎に sphere を作ってそれらを group という属性の入れ物専用のオブジェクトに格納して、どれか一つだけ表示状態にする方法です。後の案は、無駄に球データをコピーするのでメモリが無駄な気がしますが、容量のほとんどはイメージデータですので、気にするほどの差はありません。また、機能は全て three.js の基本機能で実現しますので安心感があります。今回は、この方法を採用しました。今後、もう前者の案に挑戦するかもしれません。

タグ説明

[3d_dome]
この機能のタグ名です。最初に呼ばれるとスカイドームを作り、texture で指定された天空イメージを適用します。二度目以降は、texture が既に読み込まれていた場合は、それに切り替えますが、なければ、ロードして、スカイドームに取り込み、天空イメージをそれに切り替えます。本スカイドームの使用上の注意点は、スカイドームは、シーンの中心から、レンダリング最遠の少し内側に出来ます。そのため、カメラがz軸方向に(後ろに)どんどん引くと前方にブラックホールが現れます。シーンでは、カメラはなるべくセンター(上下は問題ありません)において、その他のオブジェクトをその周りに配置するようにした方がいいです。
以下パラメータです。
texture [選択] テクスチャーのファイル名を指定します。フォルダーまで含めて記述出来ますが、フォルダー部は次の folder に任せて、ファイル名のみにするのを勧めます。理由は、テクスチャー名として使うからです。記述は、識別子までが必要です。このファイルは、folder 指定がないと ./data/others/3d/texture/ に保管されているものと見なします。指定されてない場合、テクスチャーのロード、変更はされずに、表示のオンオフのみに使われます。
folder [選択] テクスチャーの保管されているフォルダーを指定します。この指定は、dataフォルダー以下の部分の記述と見なします。デフォルトは、””です。
rot [選択] 天空イメージのy軸の回転角(ラジアン)を指定します。デフォルトは、それ以前の値です。最初の場合は、"0"です。この値は、その時点のtextureにのみ適用されます。テクスチャーは、各自の回転角を持てます。
visible [選択] スカイドームを表示するかどうかを指定します。[3d_show] では、名前を指定しないといけないので、こちらで指定します。既にスカイドームが出来ていて、テクスチャーもある場合、[3d_dome visible="true"] でスカイドームが表示されます。また、他のパラメーターも一緒に指定すると、すぐに表示されます。ただ、 [3d_show] のタグでは、表示までの遷移時間を指定できますが、このやり方だと、瞬時に表示されます。そのような効果が欲しい時は、 [3d_show] を使って、name に "__dome__" を指定してください。

コード  

$.get_fullpath = function ( name, folder, target ) {
	let f = folder != "" ? folder : "others/3d/" + target ;
	let _url = "";
	_url =
	  $.isBase64(name) || $.isHTTP(name)
		? name
		: "./data/" + f + "/" + name;
	return _url ;
};
$.create_sky_sphere = function ( name, texture, three, rot ){
	let pm ={};
	pm.name = name;
	pm.type = "SphereGeometry";
	pm.width = "64";
	pm.height = "32";
	pm.side = "double";
	pm.scale = "1";
	pm.pos = "0";
	pm.rot = rot;
	pm.arg1 = pm.radius;
	pm.arg2 = pm.width;
	pm.arg3 = pm.height;
	pm.visible = true;
	material = new THREE.MeshBasicMaterial({
		map: texture,
		alphaTest: 0.5,
		transparent: !0,
	});
	material.side = THREE.DoubleSide;
	let camera = three.camera ;
	pm.radius = camera.far * 0.95;
	const geometry = new THREE["SphereGeometry"](
		pm.radius,
		pm.width,
		pm.height,
	);
	const sphere = new THREE.Mesh(geometry, material);
	sphere.position.set(0, 0, 0);
	sphere.scale.set(1, 1, 1);
	sphere.rotation.set(0, parseFloat(pm.rot), 0);
	sphere.name = name;
	sphere.visible = true;
	/*
	three.models[pm.name] = new ThreeModel(
	  { name: pm.name, model: sphere, pm: pm },three
	);
	*/
	return sphere;
}
TYRANO.kag.tag["3d_dome"] = {
	vital : [],
	pm : {
		name:"__dome__",
		texture:"",
		//rot,
		folder:"",
		visible:"false",
	},
	start : function(pm) {
		let dome_name = pm.name;
		let three = this.kag.tmp.three;
		if ( pm.visible == "true") pm.visible=true;
		if (0 == $.checkThreeModel(dome_name)) {
			const group = new THREE.Group();
			this.kag.tmp.three.models[dome_name] = new ThreeModel(
			  { name: dome_name, model: group, pm: pm },three
			);
		}
		let	dome = this.kag.tmp.three.models[dome_name];
		if( pm.texture !=""){
			dome.model.children.forEach( (element) => element.visible = false);
			let sphere = dome.model.children.find( element => element.name == pm.texture );
			if (sphere === undefined ){
				let texture_url = $.get_fullpath( pm.texture, pm.folder, "texture");
				const texture = new THREE.TextureLoader().load(texture_url, texture => {
					if( pm.rot === undefined){
						pm.rot = "0";
					}
					sphere = $.create_sky_sphere( pm.texture, texture, three, pm.rot );
					dome.model.add( sphere );
				},);
			}
			else{
				if ( pm.rot !== undefined ){
						sphere.rotation.set( 0, parseFloat(pm.rot), 0 );
				}
				sphere.visible = true;
			}
			// save load 対策?
			dome.pm.texture = pm.texture;
		}
		if( pm.visible == true ) dome.model.visible = true;
		this.kag.ftag.nextOrder();
	},
};
TYRANO.kag.ftag.master_tag["3d_dome"] = TYRANO.kag.tag["3d_dome"];
TYRANO.kag.ftag.master_tag["3d_dome"].kag = TYRANO.kag ;

最初のコードは、本体から呼ばれる関数です。次は本体です。
本体は、ティラノスクリプトにオリジナルのタグもしくは、既存のタグの上書きをするための作法ですね。一応 スカイドームには自由な名前が付けられるようには書いてます。function(pm)以下が処理です。最初の段階で、スカイドームが無い場合、Groupという入れ子だけの Object3D を作ります。そして、this.kag.tmp.three.models という入れ物に ThreeModel というクラスで、そのオブジェクトを登録しています。このクラスは、ティラノスクリプトで3dオブジェクトを保管する入れ物です。この中に、シナリオのセーブ時に必要な物を保存するメソッドがあります。ここに登録さえすれば、一応ティラノスクリプトは管理してくれるようです。後半は、テクスチャー名が指定してあれば、既存の物かチェックし、なければロードして新規に球をつくり、スカイドームに登録します。あれば、表示対象としてvisibleをtrueにします。
跡、// save load 対策?
と書いた次のコードは、セーブされたシナリオがロードされたとき、最新のスカイドームのテクスチャーを登録するためのものです。シナリオロード時に、ティラノスクリプトは、保存されたThreeModelクラスの3dタグ名とそのパラメータでそのタグを実行するようです。


この記事が気に入ったらサポートをしてみませんか?