Android Vuforia with jPCT-AE (5) – 多重模型載入,以 obj 為例

要進到這系列最後一篇文章了,這篇文章拖了很久,一直沒有時間整理,結果到最後 … 程式碼留下來了,記憶卻有些模糊了,這邊小蛙配著程式碼盡可能的把還記得的東西寫下來。

圖片來源:http://tw.freeimages.com/photo/blank-e-box-for-software-etc-4-1238246
圖片來源:http://tw.freeimages.com/photo/blank-e-box-for-software-etc-4-1238246

設計自己的物件

因為這邊要載入多個模型,而每個模型搭配的參數都不同,例如:刀鋒戰士要放大 10 倍並且往 y 軸位移 50、茶壺要 blablabla …,這邊可以根據自己的需求去做設計,小蛙隨意列了幾個可能會隨著不同模型調整的屬性

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
class MyObjs {
    private String symbol;
    private String obj;
    private String mtl;
    private List<String> textures;
    private float scale = 1;
    private float postScale = 1;
    // 0 度躺著正面朝上
    private float rotateX = 0;
    private float rotateY = 0;
    private float rotateZ = 0;
    private float traslateX = 0;
    private float traslateY = 0;
    private float traslateZ = 0;
    private Object3D target;
}

有看不懂的可以往前翻,屬性也不一一介紹了。

初始化物件

初始化這些物件,將每個要載入的物件及他的參數一一創建出來,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 要觸發的圖片(ImageTarget)
private static List<String> SYMBOL = new ArrayList<>();
// 要呈現出來的 3D 模型
private List<MyObjs> objs = new ArrayList();
private void initObjs(){
    SYMBOL = Arrays.asList("stones", "chips", "tarmac");
    // 木頭櫃子
    MyObjs m = new MyObjs();
    m.symbol = SYMBOL.get(0);
    m.mtl = "Bedside_Table_D.mtl";
    m.obj = "Bedside Table D.obj";
    m.textures = Arrays.asList("Bedside_Table_D_default_1_1.png");
    m.scale = 5;
    m.rotateX = -1.5f;
    m.traslateX = -110;
    m.traslateY = -160;
    objs.add(m);
    // 灰色櫃子
    MyObjs w = new MyObjs();
    w.symbol = SYMBOL.get(2);
    w.mtl = "Bedside_Table_B.mtl";
    w.obj = "Bedside Table B.obj";
    w.textures = Arrays.asList("Bedside_Table_B_default_1_1.png");
    w.scale = 5;
    w.rotateX = -1.5f;
    w.traslateX = -110;
    w.traslateY = -160;
    objs.add(w);
    // 木桶
    MyObjs n = new MyObjs();
    n.symbol = SYMBOL.get(1);
    n.mtl = "MedievalBarrel_OBJ.mtl";
    n.obj = "MedievalBarrel_OBJ.obj";
    n.textures = Arrays.asList("MedBarrelDiffuse.jpg", "MedBarrelNormal.jpg");
    n.scale = 20;
    n.rotateX = -1.5f;
    n.traslateX = 0;
    n.traslateY = 0;
    objs.add(n);
}

在載入 obj 的範例中我們知道需要同時載入 mtl 搭配 textures,不然載進來的模型沒有材質貼圖。上面的例子中將屬性一一設定好,並且加入 objs 中,我們透過 m.symbol 來得知當出現哪張影像的時候,載入這個模型 (m.mtl, m.obj, m.textures), 並可設定縮放(scale)、翻轉(rotate)以及位移(traslate),這邊可以自己把自己要設定的屬性加上去。

載入物件

當把所有的物件都設定好之後,在 ImageTargetRenderer 建構子中載入這些物件們

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
... 以上省略 ...
initObjs();
try{
    for(int i = 0; i < objs.size(); i++){
        MyObjs m = objs.get(i);
        for(int j = 0; j < m.textures.size(); j++){
            TextureManager.getInstance().addTexture(
                m.textures.get(j),
                new Texture(mActivity.getAssets().open(TXT_PATH + m.textures.get(j)))
                );
        }
        Object3D[] tmp = Loader.loadOBJ(
            mActivity.getAssets().open(OBJ_PATH + m.obj),
            "".equals(m.mtl) ? null : mActivity.getAssets().open(MTL_PATH + m.mtl),
            m.scale
        );
        if(tmp != null && tmp.length >= 1){
            m.target = tmp[0];
        }
        m.target.strip();
        m.target.build();
        if(m.rotateX != 0)
            m.target.rotateX(m.rotateX);
        if(m.rotateY != 0)
            m.target.rotateY(m.rotateY);
        if(m.rotateZ != 0)
            m.target.rotateY(m.rotateZ);
        if(m.traslateX != 0 || m.traslateY != 0 || m.traslateZ != 0)
            m.target.translate(m.traslateX, m.traslateY, m.traslateZ);
        if(m.postScale != 1)
            m.target.scale(m.postScale);
        if("".equals(m.mtl)) {
            for(int z = 0; z < m.textures.size(); z++)
                m.target.setTexture(m.textures.get(z));
        }
        world.addObject(m.target);
        cam = world.getCamera();
        SimpleVector sv = new SimpleVector();
        sv.set(m.target.getTransformedCenter());
        sv.y -= 100;
        sv.z -= 100;
        sun.setPosition(sv);
    }
}catch(Exception e){
    e.printStackTrace();
}

將 objs 中的 MyObj 一一取出,並設定該物件之屬性,到這個階段,就可以依照不同影像載入不同模型,但,這還不夠。

移除模型

剩最後一個步驟,到上一個步驟會發現每個 render 出來的模型,他不會消失 … 不會消失 … 不會消失 … 加上這個步驟讓他消失吧!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// The render function.
private void renderFrame() {
    // clear color and depth buffer
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    // get the state, and mark the beginning of a rendering section
    State state = mRenderer.begin();
    // explicitly render the video background
    mRenderer.drawVideoBackground();
    float[] modelviewArray = new float[16];
    // did we find any trackables this frame?
    for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++) {
        // get the trackable
        TrackableResult result = state.getTrackableResult(tIdx);
        Trackable trackable = result.getTrackable();
        printUserData(trackable);
        Matrix44F modelViewMatrix = Tool.convertPose2GLMatrix(result.getPose());
        Matrix44F inverseMV = SampleMath.Matrix44FInverse(modelViewMatrix);
        Matrix44F invTranspMV = SampleMath.Matrix44FTranspose(inverseMV);
        try{world.removeAllObjects();}catch (Exception e){}
        for(int i = 0; i < objs.size(); i++){
            MyObjs m = objs.get(i);
            if(m.symbol.equals(trackable.getName())){
                world.addObject(m.target);
                SimpleVector sv = new SimpleVector();
                sv.set(m.target.getTransformedCenter());
                sv.y -= 100;
                sv.z -= 100;
                sun.setPosition(sv);
            }
        }
        modelviewArray = invTranspMV.getData();
        updateModelviewMatrix(modelviewArray);
    }
    // hide the objects when the targets are not detected
    if (state.getNumTrackableResults() == 0) {
        float m [] = {
            1,0,0,0,
            0,1,0,0,
            0,0,1,0,
            0,0,-10000,1
        };
        modelviewArray = m;
        updateModelviewMatrix(modelviewArray);
    }
    mRenderer.end();
}

這個 function 小蛙已經忘記哪些是小蛙自己加的,哪些是原本的 code 了,就整個附上來試試看吧!
小蛙之前也遇過有網友詢問動畫的部份,經過這一段時間的研究發現 … 如果真的要用到會動的 3D model,就直接使用 Unity 吧!畢竟在 native code 中要讓做到動畫真的太麻煩了 >< 之前 survey 了好久,因為變數太大、複雜度也太高,直接放棄轉向 render 動畫原本就是強項的 Unity。祝各位好運囉!

Android Vuforia 系列:

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *