公転させてみる

公転とは

地球が太陽の周りをまわったり、月が地球の周りをまわったりする運動を公転といいます。
つまり特定の座標を中心にその周りをぐるぐる回る運動が公転です。

しかし、Easy3Dにはモデルの向きを変える命令はありますが、公転をする命令はありません。
今回はこれを必要に駆られて作ってみました。

やり方を考えてみる

公転運動は、モデルを円軌道を描いて移動させればいいだけです。
xy平面状の楕円道は角度をtとすると、

	x = a * cos(t)
	y = b * sin(t)

円ならaとbは1.0です。
これで求めた値をE3DSetPosで設定してやればいいわけですが…

(;´Д`)メンドクセー!


やって出来ないことはありませんが、非常にめんどくさいです。
ではもっと簡単な方法を考えてみます。

軌道計算はこれを使ってみよう

Easy3DにはE3DMultQVecという便利な命令があるのでこれを使ってみることにします。
この命令は分かりやすく言うと、任意のベクトルをクォータニオンで回転させる命令です。
公転中心座標にE3DMultQVecで回転したベクトルを足してやればよさそうです。
回転もクォータニオンが勝手にやってくれるようなので簡単そうです。

こんな感じになります。

	;モデルの移動
	E3DMultQVec axisqid, dx, dy, dz, ddx, ddy, ddz
	E3DSetPos hsid, cx+ddx, cy+ddy, cz+ddz

(dx, dy, dz)は公転中心座標からモデルまでのベクトルです。
(ddx, ddy, ddz)は(dx, dy, dz)を回転した後のベクトルです。
(cx, cy, cz)は公転中心座標です。

これで座標(cx, cy, cz)を中心にモデルがぐるぐる回る動作が出来るようになります。

月のように

公転のイメージ

先ほどの方法では、モデルがカメラに対して同じ方向を向いたまま移動します。
ボックスステップをやっているような感じです。
やはり月のように地球に同じ面を向けて回転させたいところです。

さて、月が地球に対して常に同じ面を向いているのは、月自身も公転周期と同じ周期で自転しているからその相殺で同じ面を向いて見えています。
じゃあ、モデルの方も回転させましょう。先ほどのものにこれを追加します。

	E3DSetDirQ2 hsid, axisqid

公転中心座標に対して同じ方向を向けたまま回転するようになりました。

月には表もあれば裏もある

公転中心にどの面を向けるかというのは任意に決めたいですし、太陽の周りを地球が自転しながら公転するように自転もさせたいところです。
公転の回転に自転(あるいは任意の回転)を加えてやればよさそうです

クォータニオンの場合はE3DMultQを使います。
先ほどのものをこれに置き換えてみてみます。

	E3DMultQ axisqid, befqid, aftqid
	E3DSetDirQ2 hsid, axisqid

さらにいい感じになりました。

事前準備を忘れずに

書くのをすっかり忘れていましたが、クォータニオンを使用するときは事前にE3DCreateQ命令で操作用のIDを取得するのを忘れずに。

サンプル

今回のサンプルはモジュール化したものです。
モジュール化しなくてもよかったんですが、ちょっと必要があったのでやってみました。
モジュールの意味わからんという方すみません。

いつものようにタコのデータは付属サンプルのものを使用しています。
Mediaフォルダに入れておいてください。
実行に必要なファイル:e3d3004.zip

;E3D
;	キャラクターを移動する
;	任意座標を中心に回転する。
;
#include "e3dhsp3.as"
onexit goto *bye

;############################################################
#module
#deffunc mE3DInit
	;モジュールの初期化をする。
	;必ずE3DInitの後に実行すること
	E3DCreateQ resqid
	return

#deffunc mE3DRevolution int hsid, double dx, double dy, double dz, double cx, double cy, double cz, int axisqid_rev, int axisqid_model
;hsid		:対象となるモデル
;dxyz		:[実数]回転中心から規準座標へのベクトル=(回転する前の最初の座標)-(中心座標)
;cxyz		:[実数]中心座標
;axisqid_rev	:全体のクォータニオン
;axisqid_model	:モデルだけのクォータニオン

	;モデルの向きを求める→resqid
	E3DMultQ resqid, axisqid_model, axisqid_rev
	E3DSetDirQ2 hsid, resqid

	;モデルの移動
	E3DMultQVec axisqid_rev, dx, dy, dz, ddx, ddy, ddz
	E3DSetPos hsid, cx+ddx, cy+ddy, cz+ddz
	return

#global
;############################################################

	;/////////////////
	;
	;	初期化
	;
	wid = 0		;ウインドウID
	objid = -1	;オブジェクトID
	fullscreenflag = 0		;フルスクリーンフラグ
	bits = 16	;色数ビット数(fullscreenflag = 1 のときのみ有効)
	multisamplenum = 0		;アンチエイリアスのマルチサンプル数(0,2~16)
	;scid	スワップチェインID
	E3DInit wid, objid, fullscreenflag, bits, multisamplenum, scid
	mE3DInit	;モジュールの初期化
	dim keybuf, 256		;キー入力

	;/////////////////
	;
	; ライトの設定
	;
	E3DCreateLight lid1 ;光源を作成
	lightdirx1 = 1 ;平行光の向き
	lightdiry1 = -1
	lightdirz1 = 0
	lightr1 = 255	;平行光の色
	lightg1 = 255
	lightb1 = 255
	E3DSetDirectionalLight lid1, lightdirx1, lightdiry1, lightdirz1, lightr1, lightg1, lightb1 ;光源を平行光源に設定

	;/////////////////
	;
	;	カメラの初期化
	;
	camposx = 5000 : camposy = 1000 : camposz = 5000-4000
;	camposx = 0 : camposy = 0 : camposz = 4200	;デフォルト値
	E3DSetCameraPos camposx, camposy, camposz	;カメラ座標を設定
	targetx = 5000.0		;注視点座標
	targety = 500.0
	targetz = 5000.0
	upvecx = 0		;カメラの上方向のベクトル
	upvecy = 1
	upvecz = 0
	E3DSetCameraTarget targetx, targety, targetz, upvecx, upvecy, upvecz	;カメラの注視点を設定
	;プロジェクションの設定
	proj_near = 100.0 : proj_far = 50000.0 : proj_fov = 45.0
	E3DSetProjection proj_near, proj_far, proj_fov


	;/////////////////
	;
	;	地面の作成
	;
	sdim pathbuf, 2048, 4
	pathbuf.0 = dir_cur + "\\Media\\yama.bmp"	;地面の座標情報
	pathbuf.1 = dir_cur + "\\Media\\michi.bmp"	;地面の道の情報
	pathbuf.2 = dir_cur + "\\Media\\kawa.bmp"	;地面の川の情報
	pathbuf.3 = dir_cur + "\\Media\\bazou.bmp"	;地面、道、川の模様を決める、BMPファイル

	;地面作成用の値
	mapsize = 60000.0		;X,Z座標の最大値
	mapdiv = 60			;座標の分割数
	mapheight = 3000.0	;高さの最大値

	E3DLoadGroundBMP pathbuf.0, pathbuf.1, pathbuf.2, pathbuf.3, mapsize, mapsize, mapdiv, mapdiv, mapheight, hsid0
	posx0 = 0.0 : posy0 = 0.0 : posz0 = 0.0
	degx0 = 0.0 : degy0 = 0.0 : degz0 = 0.0
	E3DSetPos hsid0, posx0, posy0, posz0	;地面表示位置を設定
	E3DSetDir hsid0, degx0, degy0, degz0	;地面の向きを設定


	;/////////////////
	;
	;	形状データのロード
	;
	sdim mediadir, 2048
	mediadir = dir_cur + "\\Media\\tako.sig"
	E3DSigLoad mediadir, hsid1
	E3DSetPosOnGroundPart hsid1, hsid0,, mapheight,, 5000-1000, 5000
	E3DRotateY hsid1, 180
	E3DSetBeforePos hsid1
	E3DGetPos hsid1, sposx, sposy, sposz	;初期座標

	E3DCreateQ axisqid2
	E3DCreateQ axisqid
	E3DRotateQY axisqid, 180
	E3DSetDirQ2 hsid1, axisqid

	;/////////////////
	;
	;	公転の初期値
	;
	;中心座標
	cx = targetx : cy = targety : cz = targetz	;注視点座標を使用する。
	;モデル初期位置(中心座標を中心とした相対座標)
	sx = -1000.0 : sy = -cy : sz = 0.0


;/////////////////
;
;	メインループ
;
*main
	E3DGetKeyboardState keybuf	;キー状態取得


	E3DBeginScene	;-----シーンスタート
		E3DChkInView scid, hsid0	;モデルが、視野内にあるか判定
		E3DChkInView scid, hsid1

		if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了
		gosub *MoveChara		;キャラクター移動

		;不透明部分の描画をする
		E3DRender scid1, hsid0, 0, 0, 0	;バックバッファにレンダリングする。(地面描画)
		E3DRender scid1, hsid1, 0, 0, 0	;バックバッファにレンダリングする。(タコ描画)
		;半透明部分の描画をする
		E3DRender scid1, hsid1, 1, 0, 0, 0, 0, 1

	E3DEndScene			;-----シーン終了
	E3DPresent scid1	;バックバッファの内容を、プライマリバッファに転送。描画する。
	E3DSetBeforePos hsid1

	E3DWaitByFPS 30, chkfps1 : await 0
	title ""+chkfps1 + " FPS"

goto *main


;
;	終了処理
;
*bye
	E3DDestroyLight lid1	;ライトを破棄
	E3DBye
	end


;/////////////////
;
;	キャラクター移動
;
*MoveChara
	E3DRotateQY axisqid2, 10
	mE3DRevolution hsid1, sx, sy, sz, cx, cy, cz, axisqid2, axisqid
	return