libgdx3D第三講-場景載入


第三講 場景 載入

Loading a scene with LibGDX

說明:我是從網易雲筆記直接粘過來的。可能圖片顯示不出來,附加資源下載不了。所以能夠直接訪問上面的地址來看。
  • 資源准備
四個模型。包含之前用到的飛船、入侵者、障礙、空間場景(有紋理和反向法線。因此內部可視) invaders.zip
  • 教程開始
1.  上一講我們使用fbx-conv轉換我們的模型,對新的模型也須要這樣做。但如今我們先直接使用給出的OBJ文件。把他們復制到asset/data文件夾下,並像上次一樣載入它們。以下給出今天的代碼,並在下方解說。

public   class   LoadSceneTest  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;
      
     public   Array<ModelInstance> blocks =  new   Array<ModelInstance>();
     public   Array<ModelInstance> invaders =  new   Array<ModelInstance>();
     public   ModelInstance ship;
     public   ModelInstance space;
      
     @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(0f, 7f, 10f);
         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 );
         assets.load( "data/block.obj" , Model. class );
         assets.load( "data/invader.obj" , Model. class );
         assets.load( "data/spacesphere.obj" , Model. class );
         loading =  true ;
     }
  
     private   void   doneLoading() {
         ship =  new   ModelInstance(assets.get( "data/ship.obj" , Model. class ));
         ship.transform.setToRotation(Vector3.Y,  180 ).trn( 0 ,  0 , 6f);
         instances.add(ship);
  
         Model blockModel = assets.get( "data/block.obj" , Model. class );
         for   ( float   x = -5f; x <= 5f; x += 2f) {
             ModelInstance block =  new   ModelInstance(blockModel);
             block.transform.setToTranslation(x,  0 , 3f);
             instances.add(block);
             blocks.add(block);
         }
          
         Model invaderModel = assets.get( "data/invader.obj" , Model. class );
         for   ( float   x = -5f; x <= 5f; x += 2f) {
             for   ( float   z = -8f; z <= 0f; z += 2f) {
                 ModelInstance invader =  new   ModelInstance(invaderModel);
                 invader.transform.setToTranslation(x,  0 , z);
                 instances.add(invader);
                 invaders.add(invader);
             }
         }
          
         space =  new   ModelInstance(assets.get( "data/spacesphere.obj" , Model. class ));
          
         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);
         if   (space !=  null )
             modelBatch.render(space);
         modelBatch.end();
     }
      
     @Override
     public   void   dispose () {
         modelBatch.dispose();
         instances.clear();
         assets.dispose();
     }
      
     @Override
     public   void   resume () {
     }
   
     @Override
     public   void   resize ( int   width,  int   height) {
     }
   
     @Override
     public   void   pause () {
     }
}
如今來看代碼具體解釋:
public   Array<ModelInstance> blocks =  new   Array<ModelInstance>();
public   Array<ModelInstance> invaders =  new   Array<ModelInstance>();
public   ModelInstance ship;
public   ModelInstance space;
這里我們 添加Array存放障礙blocks和入侵者invaders,建立ModelInstance單例存飛船ship和場景space

我們依舊用它們來渲染。這樣做的優點是我們能夠非常方便的控制每一個部分。


public   void   create () {
     modelBatch =  new   ModelBatch();
     ...
     cam.position.set(0f, 7f, 10f);
     ...
     assets.load( "data/ship.obj" , Model. class );
     assets.load( "data/block.obj" , Model. class );
     assets.load( "data/invader.obj" , Model. class );
     assets.load( "data/spacesphere.obj" , Model. class );
     loading =  true ;
}
給camera設置一個更合適的位置;
assetmanager載入全部模型;
private   void   doneLoading() {
     ship =  new   ModelInstance(assets.get( "data/ship.obj" , Model. class ));
     ship.transform.setToRotation(Vector3.Y,  180 ).trn( 0 ,  0 , 6f);
     instances.add(ship);
 
     Model blockModel = assets.get( "data/block.obj" , Model. class );
     for   ( float   x = -5f; x <= 5f; x += 2f) {
         ModelInstance block =  new   ModelInstance(blockModel);
         block.transform.setToTranslation(x,  0 , 3f);
         instances.add(block);
         blocks.add(block);
     }
      
     Model invaderModel = assets.get( "data/invader.obj" , Model. class );
     for   ( float   x = -5f; x <= 5f; x += 2f) {
         for   ( float   z = -8f; z <= 0f; z += 2f) {
             ModelInstance invader =  new   ModelInstance(invaderModel);
             invader.transform.setToTranslation(x,  0 , z);
             instances.add(invader);
             invaders.add(invader);
         }
     }
      
     space =  new   ModelInstance(assets.get( "data/spacesphere.obj" , Model. class ));
      
     loading =  false ;
}
從這里開始就有趣了。第一行我們取飛船模型並建立ModelInstance
第二行我們把它回轉180度。如今它面向遠離照相機方向。然后將它在Z軸朝向相機移動6個單位;
第三行我們把它增加array中,使它能被渲染。
之后我們對block 和 invader 模型做同樣的事,但在這里我們建立多個實例。block將在x軸上排成一列,並將它們增加到兩個array中; invader將被放置在在XZ平面的網格上;
最后建立space,但我們不會將它增加array中,由於它不須要渲染光照。

public   void   render () {
     ...
     modelBatch.begin(cam);
     modelBatch.render(instances, environment);
     if   (space !=  null )
         modelBatch.render(space);
     modelBatch.end();
}
在render()方法中,我們像之前一樣 使用渲染。我們也對 space使用無燈光渲染,注意這里要檢查space是否載入好。由於它是異步載入的。

效果如圖:

看起來不錯。我們如今能夠僅僅實現一些游戲功能,然后到此結束。其實。我敢打賭。有相當多的游戲是這樣做的。但對更大的場景就不能使用了,如今我們來調整它。


2.打開你喜歡的模型應用程序(modeling application )。建立新場景。我使用Maya。這個樣例不論什么模型應用程序都能夠。

如今把四個模型導入到場景中。假設你是新手建議一個一個導入來保證正確顯示,比如手動制定紋理、翻轉紋理坐標等。

另外給每一個模型起名字,且在之后不要改名。如圖:

scenetest2


我啟用了X射線。以方便編輯,這就是為什么這些模型看起來是透明的。后面的是場景。你也能夠看到 “ship”、“block”、“invader” 模型都堆在一起,由於它們的位置都是(0,0,0)。假設全部模型都正確載入並起好名字,你能夠將其 導出成FBX格式,起名為 invaders.fbx。

模型應用程序中也能夠保存一份以備改動。


3.使用fbx-conv把FBX文件轉換為G3DB格式
fbx-conv invaders.fbx
假設你在創建FBX文件時有翻轉紋理坐標,你如今也須要將它們翻轉。那么代碼例如以下
fbx-conv -f invaders.fbx
如今把invaders.g3db復制到assets/data目錄下,我們繼續編程
public   class   LoadSceneTest  extends   GdxTest  implements   ApplicationListener {
     ...
     @Override
     public   void   create () {
         ...
         assets =  new   AssetManager();
         assets.load( "data/invaders.g3db" , Model. class );
         loading =  true ;
     }
  
     private   void   doneLoading() {
         Model model = assets.get( "data/invaders.g3db" , Model. class );
         ship =  new   ModelInstance(model,  "ship" );
         ship.transform.setToRotation(Vector3.Y,  180 ).trn( 0 ,  0 , 6f);
         instances.add(ship);
  
         for   ( float   x = -5f; x <= 5f; x += 2f) {
             ModelInstance block =  new   ModelInstance(model, "block" );
             block.transform.setToTranslation(x,  0 , 3f);
             instances.add(block);
             blocks.add(block);
         }
          
         for   ( float   x = -5f; x <= 5f; x += 2f) {
             for   ( float   z = -8f; z <= 0f; z += 2f) {
                 ModelInstance invader =  new   ModelInstance(model,  "invader" );
                 invader.transform.setToTranslation(x,  0 , z);
                 instances.add(invader);
                 invaders.add(invader);
             }
         }
          
         space =  new   ModelInstance(model,  "space" );
          
         loading =  false ;
     }
...
}
在create()方法中我們刪除了其它模型載入。換上了 invaders.g3db
在doneLoading()方法中,我們從assetmanager獲取該模型。並 創建ModelInstances,參數為model和創建FBX時所使用的名稱。以后我們會深入解說。但如今讓我們執行它。看看它的全然和曾經一樣。

這是非常實用的。由於我們能夠把全部模型放在一個場景中。並且ModelInstances僅僅載入一個模型。因此能夠共享資源,並且這使得ModelBatch(稍后詳述)性能提升。當然,假設須要的話,你仍然能夠使用多個文件,事實上有時(如與皮膚或動畫模型)使用一個單獨的文件更方便。

4.讓我們回到模型應用程序,並打開我們之前創建的場景。如今改動ship模型,繞Y軸旋轉180度。並把沿Z軸移動6格,就像在java中做的一樣。接着改動block模型。把它在Z軸移動3格,X軸移動-5格,然后重命名其為block1。改動invader模型,X軸移動-5格,重命名為invader1;復制block1五次,依次以X軸2格間距擺放。如今一共同擁有6個block,重命名其為block1-block6;再按上步同一方法復制invader1,得到下圖:

scenetest3

注意:網格間距5個單位。

5.仍然使用fbx-conv轉換,然后載入場景
public   void   create () {
     ...
     assets =  new   AssetManager();
     assets.load( "data/invaderscene.g3db" , Model. class );
     loading =  true ;
}
 
private   void   doneLoading() {
     Model model = assets.get( "data/invaderscene.g3db" , Model. class );
     for   ( int   i =  0 ; i < model.nodes.size; i++) {
         String id = model.nodes.get(i).id;
         ModelInstance instance =  new   ModelInstance(model, id);
         Node node = instance.getNode(id);
          
         instance.transform.set(node.globalTransform);
         node.translation.set( 0 , 0 , 0 );
         node.scale.set( 1 , 1 , 1 );
         node.rotation.idt();
         instance.calculateTransforms();
          
         if   (id.equals( "space" )) {
             space = instance;
             continue ;
         }
          
         instances.add(instance);
          
         if   (id.equals( "ship" ))
             ship = instance;
         else   if   (id.startsWith( "block" ))
             blocks.add(instance);
         else   if   (id.startsWith( "invader" ))
             invaders.add(instance);
     }
 
     loading =  false ;
}
取得invaders模型,遍歷節點得到每一個節點的id,然后使用model和id建立ModelInstance;

設置 ModelInstance的轉換(通常是讀取在模型應用程序中改動的旋轉和平移等)。然后我們重置節點的轉換信息。由於如今我們能夠直接讀取 ModelInstance中存放的信息。

重置的方法例如以下: translation為(0,0,0);scale為(1,1,1);rotation shiyong idt();最后調用calculateTransforms()使ModelInstance被更新。

(這段翻譯可能不太對。原文例如以下)

Next we set the transformation of the ModelInstance to the transformation of the node. Practically this reads the transformation (like rotation and translation) we set earlier within the modeling application. Then we need to reset the node’s transformation, because we now use the ModelInstance transform. For translation this is (0,0,0), for scale this is (1,1,1) and for rotation we set the quaternion to identity. This is followed by a call to calculateTransforms() to make sure the ModelInstance is updated with these new values.


把不是space的ModelInstance都增加到 array中,使其得到渲染,然后把ship、lobck、incader各增加其array中。

大功告成!




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM