前回のチュートリアルでは、3D シーンを描写するためにlibgdx を設定する方法について見てきました。 カメラを設定して光源を追加し、緑色の箱を描写しました。今回は箱を作るのではなく、modelの読み込みも行ってもっと面白いことをしてみましょう。
このチュートリアルの全ソース、 素材、実行可能なテストは この github リポジトリにあります。
お気に入りにモデリングソフトを使ったり、既存のモデルを入手して使うこともできます。 私は LibGDX gdx-invadersに付属している船のmodelを使用しました。ここにあります。 これを取り出してandroidプロジェクトのassetsフォルダー内にあるデータフィルダに置きます。 中に入っている三つのファイルは全ての同じフォルダー内に置かなければならないので注意してください。:
それでは、前回作成した箱の代わりにこのモデルを使用するようにBasic3DTestを変更してみましょう:
public class LoadModelsTest implements ApplicationListener {
...
@Override
public void create () {
modelBatch = new ModelBatch();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(1f, 1f, 1f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
ModelLoader loader = new ObjLoader();
model = loader.loadModel(Gdx.files.internal("data/ship.obj"));
instance = new ModelInstance(model);
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
}
...
}
ここでは少しだけ変更を加えます。まず最初に、船のモデルがかなり小さいのでカメラを原点に近づけます。 次にModelBuilderを削除して、代わりにModelLoaderを作成して船のモデルを読み込みます。それで完了です。
テストをする分にはこれで良いですが、より大きなアプリケーションになるとAssetManagerを使ってモデルを管理したくなるかもしれません。 ですから、AssetManagerを追加してみましょう:
public class LoadModelsTest implements ApplicationListener {
public PerspectiveCamera cam;
public CameraInputController camController;
public ModelBatch modelBatch;
public AssetManager assets;
public Array<ModelInstance> instances = new Array<ModelInstance>();
public Environment environment;
public boolean loading;
@Override
public void create () {
modelBatch = new ModelBatch();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(1f, 1f, 1f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
assets = new AssetManager();
assets.load("data/ship.obj", Model.class);
loading = true;
}
private void doneLoading() {
Model ship = assets.get("data/ship.obj", Model.class);
ModelInstance shipInstance = new ModelInstance(ship);
instances.add(shipInstance);
loading = false;
}
@Override
public void render () {
if (loading && assets.update())
doneLoading();
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instances, environment);
modelBatch.end();
}
@Override
public void dispose () {
modelBatch.dispose();
instances.clear();
assets.dispose();
}
public void resume () {
}
public void resize (int width, int height) {
}
public void pause () {
}
}
それでは、変更を行ってみましょう。 Model インスタンスを削除して AssetManagerに置き換えます。 一つだったModelInstance を配列にしました。こちらの方がより実用的で、後でより多くのインスタンスを描写できるようになります。 また、現在読み込み中かどうかを示すフラグも追加しました。
Create メソッド内では、アセットマネージャーを作成して、船のモデルを読み込むよう指示をしています。 次にloading フラグにtrueを設定し、これを基にしてassetmanagerを更新する必要があるかを判断できます。 render メソッド内では、loading フラグがtrueになっているかどうかを確認し、trueになっていればassets.update()を呼び出します。 assets.update() を実行して戻り値として true が返ってきた場合は読み込みが完了したと分かるので、doneLoading()という今回新しく追加したメソッドを呼び出します。 また render メソッド内では、一つだけではなくて全てのインスタンスを描写します。 素材の読み込みが完了してない場合、この配列は空です。
doneLoading()メソッドでは読み込みが完了した船のモデルを取得し、shipInstance という名前のインスタンスを作成し、それをインスタンスの配列に追加し、描写を行えるようにします。 最後に、loading フラグにfalseを設定する必要があります。これで assets.update()メソッドはこれ以上呼び出されることはありません。
これを実行すると、前回と同じ出力結果が表示されます。 船が表示される前に少しの間黒い画面が表示されるかもしれませんが、これは現時点ではモデルが非同期で読み込まれていることが原因です。
これで複数のModel インスタンスをサポートするようになったので、もう少し追加してみましょう。
public class LoadModelsTest implements ApplicationListener {
...
@Override
public void create () {
...
cam.position.set(7f, 7f, 7f);
...
}
private void doneLoading() {
Model ship = assets.get("data/ship.obj", Model.class);
for (float x = -5f; x <= 5f; x += 2f) {
for (float z = -5f; z <= 5f; z += 2f) {
ModelInstance shipInstance = new ModelInstance(ship);
shipInstance.transform.setToTranslation(x, 0, z);
instances.add(shipInstance);
}
}
loading = false;
}
...
}
上記コードではカメラを原点から遠ざけているので、全ての船を見ることができます。 マウスをスクロールしてズームインやズームアウトを行うこともできるので覚えておいてください。 今回のdoneLoading メソッドでは複数のインスタンスを作成して、それをXZ 平面上で格子状に配置しています。
テストでは obj (wavefront) ファイルを使用するのが良いです。 しかし、このファイルには複雑なモデルを描写するのに十分な情報が含まれていないため、実際のアプリケーションで使用するのはふさわしくありません。 実際、LibGDX に含まれているObjLoader はテストのみを目的としており、あなたが使用したいであろう全ての機能を実装しているわけではありません。
幸いなことに、モデリングソフトからエクスポートしたモデルをLibGDXでの描写に適した形式に変換するfbx-convがあります。 その名前とは違って、 fbx-conv はfbx形式以外にも多くのファイル形式(objを含む)の変換を行うことができます。 fbxはほぼ全てのモデリングソフトでサポートしている形式なので、望ましい形式ではありますが。 LibGDX がサポートしているファイル形式は、 g3dj (簡単にデバッグができる json テキスト ) とg3db (軽量で読み込みが早いので、リリース時に使用すべきバイナリ)の二つがあります。
それでは、船のモデルを変換してみましょう。 fbx-conv をダウンロードして、次を実行してください:[java]fbx-conv ship.obj[/java]。 他のファイル (ship.mtlなど) が同じフォルダ内にあることを確認してください。 実際の実行ファイルには fbx-conv-win32のような別の名前が付けられている可能性もあるので注意してください。 その場合は、実際に付けられている正しい名前を使ってください。 ここでは、あなたが開発環境でコマンドライン (CLI)ユーティリティを実行する方法を知っているものとして説明しています。 知らない場合は、コマンドラインの使用方法を勉強することをお勧めします。 fbx-convにfbx ファイルをドラッグアンドドロップする、といったことは行わないでください。そんなことしても変換はできません。
これで、テストアプリケーションで使用可能なship.g3dbという名前のファイルができました。:
public class LoadModelsTest implements ApplicationListener {
...
@Override
public void create () {
...
assets.load("data/ship.g3db", Model.class);
...
}
private void doneLoading() {
Model ship = assets.get("data/ship.g3db", Model.class);
...
}
...
}