UP | HOME

8.4 glTFモデルの移動・回転

ユーザインタフェースを介して、glTFモデルを移動・回転させる方法を説明します。

1. サンプルコード

ユーザインタフェースを介して、glTFモデルを移動・回転させる glTFModelController.html 及び、 glTFModelController.js のサンプルコードとシーンファイル( glTFController.json )です。 このサンプルコードでは、対応するボタンを操作することで、前進、後進、左90度回転、右90度回転ができます。

1.1. glTFデータの入手

Sketchfabへアクセスし、glTFファイルフォーマットのデータをダウンロードする、 もしくはダウンロードリンクをクリックしてダウンロードしてください。 ダウンロードリンクからダウンロードした場合はzipファイルを展開してご利用ください。 展開したデータは解凍した結果できたディレクトリを含めて、mapray-jsのルートディレクトリからの相対パスで以下のディレクトリに保存されているという想定で以下の説明を行います。

./examples/entity/gltf/data/

なお、データは当社の著作物ではありません。著作権は各データの作成者に帰属します。詳細はフォルダ中のLICENSEファイルを参照の上ご利用ください。 ユーザーの皆様がコンテンツの権利を侵害した場合、当社では一切責任を追うものではありませんのでご注意ください。

1.2. glTFModelController.html

 1: <!DOCTYPE html>
 2: <html>
 3:     <head>
 4:         <meta charset="utf-8">
 5:         <title>glTFModelControllerSample</title>
 6:         <script src="https://resource.mapray.com/mapray-js/v0.9.4/mapray.min.js"></script>
 7:         <link rel="stylesheet" href="https://resource.mapray.com/styles/v1/mapray.css">
 8:         <script src="glTFModelController.js"></script>
 9:         <style>
10:             html, body {
11:                 height: 100%;
12:                 margin: 0;
13:                 background-color: #E0E0E0;
14:             }
15: 
16:             input {
17:                 font-size: 13px;
18:                 width: 80px;
19:                 height: 30px;
20:             }
21: 
22:             div#mapray-container {
23:                 display: flex;
24:                 position: relative;
25:                 height: calc(100% - 102px);
26:             }
27: 
28:             div#ButtonBox {
29:                 display: block;
30:                 background-color: #E0E0E0;
31:                 height: 100px;
32:                 width: 250px;
33:                 border: inset 1px #000000;
34:                 float: left;
35:             }
36: 
37:             div#ForwardButton{
38:                 display:flex;
39:                 width: 80px;
40:                 height: 30px;
41:                 margin-top: 5px;
42:                 margin-left: 85px;
43:             }
44: 
45:             div#RotateLeft90Button{
46:                 display: flex;
47:                 width: 80px;
48:                 height: 30px;
49:                 margin-left: 5px;
50:                 float: left;
51:             }
52: 
53:             div#RotateRight90Button{
54:                 display: flex;
55:                 width: 80px;
56:                 height: 30px;
57:                 margin-left:165px;
58:             }
59: 
60:             div#BackwardButton{
61:                 display: flex;
62:                 width: 80px;
63:                 height: 30px;
64:                 margin-left: 85px;
65:             }
66:         </style>
67:     </head>
68: 
69:     <body onload="CreateModelControllerInstance('mapray-container');">
70:         <div id="mapray-container"></div>
71: 
72:         <div id="ButtonBox">
73:             <div id="ForwardButton"><input type="button" value="Forward" onclick="ForwardButtonClicked()"></div>
74:             <div id="RotateLeft90Button"><input type="button" value="Left Turn" onclick="LeftTurnButtonClicked()"></div>
75:             <div id="RotateRight90Button"><input type="button" value="Right Turn" onclick="RightTurnButtonClicked()"></div>
76:             <div id="BackwardButton"><input type="button" value="Backward" onclick="BackwardButtonClicked()"></div>
77:         </div>
78:     </body>
79: </html>

1.3. glTFModelController.js

  1: var model_Controller;
  2: 
  3: class ModelController {
  4:     constructor(container) {
  5:         // Access Tokenを設定
  6:         var accessToken = "<your access token here>";
  7: 
  8:         // Viewerを作成する
  9:         this.viewer = new mapray.Viewer(
 10:             container, {
 11:                 image_provider: this.createImageProvider(),
 12:                 dem_provider: new mapray.CloudDemProvider(accessToken)
 13:             }
 14:         );
 15: 
 16:         // glTFモデルのライセンス表示
 17:         this.viewer.attribution_controller.addAttribution( {
 18:             display: "Created by modifying truck-wip by Renafox: Creative Commons - Attribution",
 19:             link: "https://sketchfab.com/3d-models/truck-wip-33e925207e134652bd8c2465e5c16957"
 20:         } );
 21: 
 22:         this.model_Point = new mapray.GeoPoint(135.759309, 35.025891, 55.0);    // モデルの球面座標(経度、緯度、高度)
 23:         this.move_Vec = [0, 1, 0];                                              // モデルの移動方向(X:経度 Y:緯度 Z:高度)
 24:         this.model_Angle = 0;                                                   // モデルの向いている向き(Z軸回転)
 25:         this.isLoadedModel = false;                                             // モデルをロードできたか
 26:         this.move_Correction = 0.00007;                                         // 移動量の補正値
 27: 
 28:         this.SetCamera();
 29: 
 30:         this.LoadScene();
 31:     }
 32: 
 33:     // 画像プロバイダを生成
 34:     createImageProvider() {
 35:         // 国土地理院提供の汎用的な地図タイルを設定
 36:         return new mapray.StandardImageProvider("https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/", ".jpg", 256, 2, 18);
 37:     }
 38: 
 39:     // カメラ位置の設定
 40:     SetCamera() {
 41:         // 球面座標系(経度、緯度、高度)で視点を設定。座標は京都御所前
 42:         var home_pos = { longitude: 135.759366, latitude: 35.025891, height: 50.0 };
 43: 
 44:         // 球面座標から地心直交座標へ変換
 45:         var home_view_geoPoint = new mapray.GeoPoint( home_pos.longitude, home_pos.latitude, home_pos.height );
 46:         var home_view_to_gocs = home_view_geoPoint.getMlocsToGocsMatrix( mapray.GeoMath.createMatrix() );
 47: 
 48:         // 視線方向を定義
 49:         var cam_pos = mapray.GeoMath.createVector3([-400, 10, 400]);
 50:         var cam_end_pos = mapray.GeoMath.createVector3([0, 0, 0]);
 51:         var cam_up = mapray.GeoMath.createVector3([0, 0, 1]);
 52: 
 53:         // ビュー変換行列を作成
 54:         var view_to_home = mapray.GeoMath.createMatrix();
 55:         mapray.GeoMath.lookat_matrix(cam_pos, cam_end_pos, cam_up, view_to_home);
 56: 
 57:         // カメラの位置と視線方向からカメラの姿勢を変更
 58:         var view_to_gocs = this.viewer.camera.view_to_gocs;
 59:         mapray.GeoMath.mul_AA(home_view_to_gocs, view_to_home, view_to_gocs);
 60: 
 61:         // カメラのnear  farの設定
 62:         this.viewer.camera.near = 30;
 63:         this.viewer.camera.far = 500000;
 64:     }
 65: 
 66:     // シーンの読み込み
 67:     LoadScene() {
 68:         var scene_File_URL = "./data/glTFController.json";
 69: 
 70:         // シーンを読み込む
 71:         var loader = new mapray.SceneLoader(this.viewer.scene, scene_File_URL, {
 72:             transform: (url, type) => this.onTransform(url, type),
 73:             callback: (loader, isSuccess) => {
 74:                 this.onLoadScene(loader, isSuccess);
 75:             }
 76:         });
 77: 
 78:         loader.load();
 79:     }
 80: 
 81:     onTransform(url, type) {
 82:         return {
 83:             url: url,
 84:             credentials: mapray.CredentialMode.SAME_ORIGIN,
 85:             headers: {}
 86:         };
 87:     }
 88: 
 89:     onLoadScene(loader, isSuccess) {
 90:         if (isSuccess) {
 91:             this.isLoadedModel = true;
 92: 
 93:             this.UpdateModelPosition();
 94:         }
 95:     }
 96: 
 97:     UpdateModelPosition() {
 98:         // sceneのEntityを取得
 99:         var entity = this.viewer.scene.getEntity(0);
100: 
101:         // モデルの位置を設定
102:         entity.setPosition(this.model_Point);
103: 
104:         // モデルの回転
105:         entity.setOrientation(new mapray.Orientation(-this.model_Angle, 0, 0));
106:     }
107: 
108:     UpdateMoveVec() {
109:         // モデルの回転行列を求める
110:         var rot_mat = mapray.GeoMath.rotation_matrix([0, 0, 1], this.model_Angle, mapray.GeoMath.createMatrix());
111: 
112:         // 移動マトリックス生成
113:         var move_mat = mapray.GeoMath.createMatrix();
114: 
115:         // 単位行列に変換
116:         mapray.GeoMath.setIdentity(move_mat);
117: 
118:         // Yの平行成分を更新
119:         move_mat[13] = 1;
120: 
121:         // 移動マトリックスに回転行列をかける
122:         var rot_move_mat = mapray.GeoMath.mul_AA(rot_mat, move_mat, mapray.GeoMath.createMatrix());
123: 
124:         // 回転後マトリックスから座標を取得
125:         this.move_Vec = [rot_move_mat[12], rot_move_mat[13], rot_move_mat[14]];
126:     }
127: 
128:     Forward() {
129:         if (this.isLoadedModel == false) {
130:             return;
131:         }
132: 
133:         this.model_Point.longitude += this.move_Vec[0] * this.move_Correction;
134:         this.model_Point.latitude += this.move_Vec[1] * this.move_Correction;
135: 
136:         this.UpdateModelPosition();
137:     }
138: 
139:     Backward() {
140:         if (this.isLoadedModel == false) {
141:             return;
142:         }
143: 
144:         this.model_Point.longitude -= this.move_Vec[0] * this.move_Correction;
145:         this.model_Point.latitude -= this.move_Vec[1] * this.move_Correction;
146: 
147:         this.UpdateModelPosition();
148:     }
149: 
150:     LeftTurn() {
151:         if (this.isLoadedModel == false) {
152:             return;
153:         }
154: 
155:         this.model_Angle += 90;
156: 
157:         this.UpdateMoveVec();
158: 
159:         this.UpdateModelPosition();
160:     }
161: 
162:     RightTurn() {
163:         if (this.isLoadedModel == false) {
164:             return;
165:         }
166: 
167:         this.model_Angle -= 90;
168: 
169:         this.UpdateMoveVec();
170: 
171:         this.UpdateModelPosition();
172:     }
173: 
174: }
175: 
176: function CreateModelControllerInstance(container) {
177:     model_Controller = new ModelController(container);
178: }
179: 
180: function ForwardButtonClicked() {
181:     model_Controller.Forward();
182: }
183: 
184: function BackwardButtonClicked() {
185:     model_Controller.Backward();
186: }
187: 
188: function LeftTurnButtonClicked() {
189:     model_Controller.LeftTurn();
190: }
191: 
192: function RightTurnButtonClicked() {
193:     model_Controller.RightTurn();
194: }

1.4. シーンファイル(glTFController.json)

 1: {
 2:   "model_register": {
 3:     "model-0": {
 4:       "link": "./truck_wip/scene.gltf",
 5:       "offset_transform": { "tilt": -90, "scale": 0.1 }
 6:       }
 7:     },
 8:   "entity_list": [{
 9:     "type": "model",
10:     "mode": "basic",
11:     "transform": { "position": [135.759366, 35.025891, 55.0] },
12:     "ref_model": "model-0",
13:     "altitude_mode": "absolute"
14:     }
15:   ]
16: }

2. htmlのサンプルコードの詳細

htmlのサンプルコードの詳細を以下で解説します。

2.1. htmlの文字コード設定

4行目でhtmlの文字コードを設定します。このサンプルコードでは、utf-8を設定します。

4: <meta charset="utf-8">

2.2. タイトルの設定

5行目でタイトルの設定をします。このサンプルコードでは、glTFModelControllerSampleを設定します。

5: <title>glTFModelControllerSample</title>

2.3. JavaScriptファイルのパス設定

6~8行目で参照するJavaScript及びスタイルシートのパスを設定します。このサンプルコードでは、maprayのJavaScriptファイル、スタイルシート、glTFモデルを操作するJavaScriptファイル( glTFModelController.js )を設定します。

6: <script src="https://resource.mapray.com/mapray-js/v0.9.4/mapray.min.js"></script>
7: <link rel="stylesheet" href="https://resource.mapray.com/styles/v1/mapray.css">
8: <script src="glTFModelController.js"></script>

2.4. スタイルの設定

9~66行目で表示する要素のスタイルを設定します。このサンプルコードでは、下記のスタイルを設定します。

  • html
  • body
  • input
  • div#mapray-container(地図表示部分)
  • div#ButtonBox(モデル操作ボタン表示部分)
  • div#ForwardButton(前進ボタン表示部分)
  • div#RotateLeft90Button(左回転ボタン表示部分)
  • div#RotateRight90Button(右回転ボタン表示部分)
  • div#BackwardButton(後進ボタン表示部分)
 9: <style>
10:     html, body {
11:         height: 100%;
12:         margin: 0;
13:         background-color: #E0E0E0;
14:     }
15: 
16:     input {
17:         font-size: 13px;
18:         width: 80px;
19:         height: 30px;
20:     }
21: 
22:     div#mapray-container {
23:         display: flex;
24:         position: relative;
25:         height: calc(100% - 102px);
26:     }
27: 
28:     div#ButtonBox {
29:         display: block;
30:         background-color: #E0E0E0;
31:         height: 100px;
32:         width: 250px;
33:         border: inset 1px #000000;
34:         float: left;
35:     }
36: 
37:     div#ForwardButton{
38:         display:flex;
39:         width: 80px;
40:         height: 30px;
41:         margin-top: 5px;
42:         margin-left: 85px;
43:     }
44: 
45:     div#RotateLeft90Button{
46:         display: flex;
47:         width: 80px;
48:         height: 30px;
49:         margin-left: 5px;
50:         float: left;
51:     }
52: 
53:     div#RotateRight90Button{
54:         display: flex;
55:         width: 80px;
56:         height: 30px;
57:         margin-left:165px;
58:     }
59: 
60:     div#BackwardButton{
61:         display: flex;
62:         width: 80px;
63:         height: 30px;
64:         margin-left: 85px;
65:     }
66: </style>

2.5. loadイベントの設定

画面を表示するときに、glTFモデルを操作するクラスを生成します。そのため、69行目でページ読み込み時に、glTFモデルを操作するクラスのインスタンスを生成する関数( CreateglTFModelControllerInstance )を呼ぶように設定します。 glTFモデルを操作するクラスのインスタンスを生成する関数は、JavaScriptのサンプルコードの詳細で説明します。

69: <body onload="CreateModelControllerInstance('mapray-container');">

2.6. 地図表示部分の指定

70行目で地図表示部分のブロックを記述します。 詳細はヘルプページ『緯度経度によるカメラ位置の指定』を参照してください。

70: <div id="mapray-container"></div>

2.7. モデル操作のUI

72~77行目でモデル操作ボタン表示部分のブロックを記述します。このブロックの中には、前進ボタン、左回転ボタン、右回転ボタン、後退ボタンを用意します。 前進ボタンをクリックした時のイベント(onclick)に、前進ボタンクリック時に呼び出す関数(ForwardButtonClicked)を設定します。同様に、各ボタンのクリック時に呼び出す関数(LeftTurnButtonClicked、RightTurnButtonClicked、BackwardButtonClicked)をそれぞれ設定します。 各ボタンのクリック時に呼び出す関数は、JavaScriptのサンプルコードの詳細で説明します。

72: <div id="ButtonBox">
73:     <div id="ForwardButton"><input type="button" value="Forward" onclick="ForwardButtonClicked()"></div>
74:     <div id="RotateLeft90Button"><input type="button" value="Left Turn" onclick="LeftTurnButtonClicked()"></div>
75:     <div id="RotateRight90Button"><input type="button" value="Right Turn" onclick="RightTurnButtonClicked()"></div>
76:     <div id="BackwardButton"><input type="button" value="Backward" onclick="BackwardButtonClicked()"></div>
77: </div>

3. JavaScriptのサンプルコードの詳細

JavaScriptのサンプルコードの詳細を以下で解説します。

3.1. クラスとグローバル変数の説明

3~174行目でglTFモデルを操作するクラスを定義します。クラス内の各メソッドの詳細は以降で解説します。 また、1行目でglTFモデルを操作するクラスのグローバル変数を定義します。

var model_Controller;

class ModelController {

    //中略

}

3.2. コンストラクタ

4~31行目がglTFモデルを操作するクラスのコンストラクタです。 まず、引数として渡されるブロックのidに対して、mapray.Viewerを作成し、glTFモデルの出典情報を追加します。mapray.Viewerのベース地図の画像プロバイダは、画像プロバイダの生成メソッドで取得した画像プロバイダを設定します。mapray.Viewerの作成の詳細は、ヘルプページ『緯度経度によるカメラ位置の指定』を参照してください。 次に、glTFモデルの操作に関する初期値を下記のように設定します。

  • glTFモデル初期位置の緯度、経度、高度 ⇒ 京都御所沿いの道路
  • glTFモデルの現在移動方向(経度・緯度・高度) ⇒ 緯度+方向
  • glTFモデルの現在の向き ⇒ 0度
  • glTFモデルのロードの成功可否 ⇒ false
  • glTFモデルの移動量 ⇒ 0.00007度

最後に、カメラの位置・向きの設定、シーンのロードの順にメソッドを呼び出します。

 4: constructor(container) {
 5:     // Access Tokenを設定
 6:     var accessToken = "<your access token here>";
 7: 
 8:     // Viewerを作成する
 9:     this.viewer = new mapray.Viewer(
10:         container, {
11:             image_provider: this.createImageProvider(),
12:             dem_provider: new mapray.CloudDemProvider(accessToken)
13:         }
14:     );
15: 
16:     // glTFモデルのライセンス表示
17:     this.viewer.attribution_controller.addAttribution( {
18:         display: "Created by modifying truck-wip by Renafox: Creative Commons - Attribution",
19:         link: "https://sketchfab.com/3d-models/truck-wip-33e925207e134652bd8c2465e5c16957"
20:     } );
21: 
22:     this.model_Point = new mapray.GeoPoint(135.759309, 35.025891, 55.0);    // モデルの球面座標(経度、緯度、高度)
23:     this.move_Vec = [0, 1, 0];                                              // モデルの移動方向(X:経度 Y:緯度 Z:高度)
24:     this.model_Angle = 0;                                                   // モデルの向いている向き(Z軸回転)
25:     this.isLoadedModel = false;                                             // モデルをロードできたか
26:     this.move_Correction = 0.00007;                                         // 移動量の補正値
27: 
28:     this.SetCamera();
29: 
30:     this.LoadScene();
31: }

3.3. 画像プロバイダを生成

34~37行目が画像プロバイダの生成メソッドです。生成した画像プロバイダを返します。 画像プロバイダの生成の詳細は、ヘルプページ『緯度経度によるカメラ位置の指定』を参照してください。

33: // 画像プロバイダを生成
34: createImageProvider() {
35:     // 国土地理院提供の汎用的な地図タイルを設定
36:     return new mapray.StandardImageProvider("https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/", ".jpg", 256, 2, 18);
37: }

3.4. カメラの位置・向きの設定

40~64行目がカメラの位置・向きの設定メソッドです。 カメラの位置・向きの設定は、ヘルプページ『緯度経度によるカメラ位置の指定』を参照してください。

39: // カメラ位置の設定
40: SetCamera() {
41:     // 球面座標系(経度、緯度、高度)で視点を設定。座標は京都御所前
42:     var home_pos = { longitude: 135.759366, latitude: 35.025891, height: 50.0 };
43: 
44:     // 球面座標から地心直交座標へ変換
45:     var home_view_geoPoint = new mapray.GeoPoint( home_pos.longitude, home_pos.latitude, home_pos.height );
46:     var home_view_to_gocs = home_view_geoPoint.getMlocsToGocsMatrix( mapray.GeoMath.createMatrix() );
47: 
48:     // 視線方向を定義
49:     var cam_pos = mapray.GeoMath.createVector3([-400, 10, 400]);
50:     var cam_end_pos = mapray.GeoMath.createVector3([0, 0, 0]);
51:     var cam_up = mapray.GeoMath.createVector3([0, 0, 1]);
52: 
53:     // ビュー変換行列を作成
54:     var view_to_home = mapray.GeoMath.createMatrix();
55:     mapray.GeoMath.lookat_matrix(cam_pos, cam_end_pos, cam_up, view_to_home);
56: 
57:     // カメラの位置と視線方向からカメラの姿勢を変更
58:     var view_to_gocs = this.viewer.camera.view_to_gocs;
59:     mapray.GeoMath.mul_AA(home_view_to_gocs, view_to_home, view_to_gocs);
60: 
61:     // カメラのnear、farの設定
62:     this.viewer.camera.near = 30;
63:     this.viewer.camera.far = 500000;
64: }

3.5. シーンのロード

67~79行目がシーンのロードメソッドです。 シーンのロードは、ヘルプページ『glTFモデルの表示(SceneLoaderを使った表示)』を参照してください。

66: // シーンの読み込み
67: LoadScene() {
68:     var scene_File_URL = "./data/glTFController.json";
69: 
70:     // シーンを読み込む
71:     var loader = new mapray.SceneLoader(this.viewer.scene, scene_File_URL, {
72:         transform: (url, type) => this.onTransform(url, type),
73:         callback: (loader, isSuccess) => {
74:             this.onLoadScene(loader, isSuccess);
75:         }
76:     });
77: 
78:     loader.load();
79: }

3.6. リソース要求変換

81~87行目がリソース要求変換メソッドです。 リソース要求変換は、ヘルプページ『glTFモデルの表示(SceneLoaderを使った表示)』を参照してください。

81: onTransform(url, type) {
82:     return {
83:         url: url,
84:         credentials: mapray.CredentialMode.SAME_ORIGIN,
85:         headers: {}
86:     };
87: }

3.7. シーンのロード終了イベント

89~95行目がシーンのロード終了イベントメソッドです。引数のisSuccessには、読み込み結果が格納されており、trueの場合のみ読み込んだglTFモデルを表示し、glTFモデルを操作できるようにします。 glTFモデルのロード成功可否をtrueにし、glTFモデルの表示位置を設定するメソッドを呼び出します。glTFモデルの表示位置を設定するメソッドの詳細は後述します。

89: onLoadScene(loader, isSuccess) {
90:     if (isSuccess) {
91:         this.isLoadedModel = true;
92: 
93:         this.UpdateModelPosition();
94:     }
95: }

3.8. glTFモデルの表示位置の設定

97~106行目がglTFモデルの表示位置の設定メソッドです。モデルの表示位置、向きをモデルのエンティティに反映します。 102行目でモデルの表示位置を設定、105行目でモデルの向きをそれぞれ設定します。 なお、読み込んだモデルは1つ目のエンティティとなるため、エンティティ取得時の引数には0を指定します。

 97: UpdateModelPosition() {
 98:     // sceneのEntityを取得
 99:     var entity = this.viewer.scene.getEntity(0);
100: 
101:     // モデルの位置を設定
102:     entity.setPosition(this.model_Point);
103: 
104:     // モデルの回転
105:     entity.setOrientation(new mapray.Orientation(-this.model_Angle, 0, 0));
106: }

3.9. 移動方向ベクトルの作成

108~126行目が移動方向ベクトル作成メソッドです。現在の向きを表す回転行列を利用して、最新の移動方向ベクトルを求めます。 まず、110~119行目で、移動方向を表す変換行列を単位行列に初期化し、glTFモデルの初期の前方向であるY軸方向に単位移動量である1分移動させます。その後、移動方向を表す変換行列に、現在の向きを表す変換行列を掛け合わせることで、現在の移動方向を表す変換行列を求めます。 そして、求めた現在の移動方向を表す変換行列から、移動方向成分に該当する部分を抜き出し、移動方向ベクトルとして設定します。

108: UpdateMoveVec() {
109:     // モデルの回転行列を求める
110:     var rot_mat = mapray.GeoMath.rotation_matrix([0, 0, 1], this.model_Angle, mapray.GeoMath.createMatrix());
111: 
112:     // 移動マトリックス生成
113:     var move_mat = mapray.GeoMath.createMatrix();
114: 
115:     // 単位行列に変換
116:     mapray.GeoMath.setIdentity(move_mat);
117: 
118:     // Yの平行成分を更新
119:     move_mat[13] = 1;
120: 
121:     // 移動マトリックスに回転行列をかける
122:     var rot_move_mat = mapray.GeoMath.mul_AA(rot_mat, move_mat, mapray.GeoMath.createMatrix());
123: 
124:     // 回転後マトリックスから座標を取得
125:     this.move_Vec = [rot_move_mat[12], rot_move_mat[13], rot_move_mat[14]];
126: }

3.10. 前進

128~137行目が前進メソッドです。glTFモデルが正常に読み込まれている場合は、現在のglTFモデルの移動方向ベクトルに移動量を掛けた値をモデルの緯度・経度に加算し、glTFモデルの姿勢変換行列の設定メソッドを呼び出します。

128: Forward() {
129:     if (this.isLoadedModel == false) {
130:         return;
131:     }
132: 
133:     this.model_Point.longitude += this.move_Vec[0] * this.move_Correction;
134:     this.model_Point.latitude += this.move_Vec[1] * this.move_Correction;
135: 
136:     this.UpdateModelPosition();
137: }

3.11. 後進

139~148行目が後退メソッドです。glTFモデルが正常に読み込まれている場合は、現在のglTFモデルの移動方向ベクトルに移動量を掛けた値をモデルの緯度・経度に減算し、glTFモデルの姿勢変換行列の設定メソッドを呼び出します。

139: Backward() {
140:     if (this.isLoadedModel == false) {
141:         return;
142:     }
143: 
144:     this.model_Point.longitude -= this.move_Vec[0] * this.move_Correction;
145:     this.model_Point.latitude -= this.move_Vec[1] * this.move_Correction;
146: 
147:     this.UpdateModelPosition();
148: }

3.12. 左回転

150~160行目が左回転メソッドです。glTFモデルが正常に読み込まれている場合は、現在のglTFモデルの回転角度に90を加算し、glTFモデルの姿勢変換行列の設定メソッドを呼び出します。

150: LeftTurn() {
151:     if (this.isLoadedModel == false) {
152:         return;
153:     }
154: 
155:     this.model_Angle += 90;
156: 
157:     this.UpdateMoveVec();
158: 
159:     this.UpdateModelPosition();
160: }

3.13. 右回転

162~172行目が右回転メソッドです。glTFモデルが正常に読み込まれている場合は、現在のglTFモデルの回転角度に90を減算し、glTFモデルの姿勢変換行列の設定メソッドを呼び出します。

162: RightTurn() {
163:     if (this.isLoadedModel == false) {
164:         return;
165:     }
166: 
167:     this.model_Angle -= 90;
168: 
169:     this.UpdateMoveVec();
170: 
171:     this.UpdateModelPosition();
172: }

3.14. glTFモデルを操作するクラスのインスタンス生成

176~178行目の関数は、引数として渡されるブロックのidを利用して、glTFモデルを操作するクラスのインスタンスを生成します。

176: function CreateModelControllerInstance(container) {
177:     model_Controller = new ModelController(container);
178: }

3.15. 前進ボタンクリック時のイベント

180~182行目の関数は、前進ボタンクリック時に呼ばれ、glTFモデルを操作するクラスの前進メソッドを呼び出します。

180: function ForwardButtonClicked() {
181:     model_Controller.Forward();
182: }

3.16. 後進ボタンクリック時のイベント

184~186行目の関数は、後進ボタンクリック時に呼ばれ、glTFモデルを操作するクラスの後進メソッドを呼び出します。

184: function BackwardButtonClicked() {
185:     model_Controller.Backward();
186: }

3.17. 左回転ボタンクリック時のイベント

188~190行目の関数は、左回転ボタンクリック時に呼ばれ、glTFモデルを操作するクラスの左回転メソッドを呼び出します。

188: function LeftTurnButtonClicked() {
189:     model_Controller.LeftTurn();
190: }

3.18. 右回転ボタンクリック時のイベント

192~194行目の関数は、右回転ボタンクリック時に呼ばれ、glTFモデルを操作するクラスの左回転メソッドを呼び出します。

192: function RightTurnButtonClicked() {
193:     model_Controller.RightTurn();
194: }

4. シーンファイルの詳細

シーンファイルの詳細を以下で解説します。なお、シーンファイルはJSON形式で記述します。

4.1. エンティティの設定

8行目でentity_listという名称でエンティティを定義し、その中にエンティティの詳細を定義します。9行目のtypeという名称は、エンティティの種類を表し、glTFモデルの場合はmodelを指定します。

{

  中略

  "entity_list": [{
    "type": "model",

      中略

    }
  ]
}

4.2. glTFモデルのデータ

2~7行目でmodel_registerという名称でモデルデータを定義します。このシーンファイルでは、モデルデータのIDをmodel-0とし、モデルファイルをファイルから読み込むために、linkという名称にglTFファイルのURLを指定します。 また、モデルの初期向きとスケールをoffset_transformという名称で指定することができます。今回のデータでは下記の内容を定義します。

  • チルト(Y軸回りの回転角度)(tilt) ⇒ -90
  • モデルスケール(scale) ⇒ 0.1
2: "model_register": {
3:   "model-0": {
4:     "link": "./truck_wip/scene.gltf",
5:     "offset_transform": { "tilt": -90, "scale": 0.1 }
6:     }
7:   },

4.3. 汎用エンティティの設定

8~15行目で汎用エンティティの設定をします。汎用エンティティには以下の内容を定義します。

  • モード(mode) ⇒ basic
  • 初期姿勢(transform) ⇒ 球面座標系(position)での初期位置
  • モデルデータ(ref_model) ⇒ モデルデータのID(model-0)
  • 高度モード(altitude_mode) ⇒ 初期位置の高度を絶対値で指定(absolute)
 8: "type": "model",
 9: "mode": "basic",
10: "transform": { "position": [135.759366, 35.025891, 55.0] },
11: "ref_model": "model-0",
12: "altitude_mode": "absolute"

5. 出力イメージ

このサンプルコードで初期状態の出力イメージは下図のようになります。 SampleImageglTFModelController.png

初期状態から前進ボタンを5回押した時の出力イメージは下図のようになります。 SampleImageglTFModelController_forward5.png

初期状態から後進ボタンを5回押した時の出力イメージは下図のようになります。 SampleImageglTFModelController_backward5.png

初期状態から左回転ボタンを押した時の出力イメージは下図のようになります。 SampleImageglTFModelController_leftTurn.png

初期状態から右回転ボタンを押した時の出力イメージは下図のようになります。 SampleImageglTFModelController_rightTurn.png