Search code examples
android3dgltf

Android: Google Filament GLTF/GLB model not displayed


My issue is that I'm trying to make a SurfaceView display a 3D model that's just a small part of the whole application layout. The layout includes multiple views for displaying other data; however, as soon as I create the ModelViewer the SurfaceView vanishes. When I use version 1.9.9 of Filament the layout is displayed, but heavily pixelated and seems to make other layout elements disappear. With v1.7.0 the other elements don't dissapear, but nothing is displayed.

I'm using the following version in Gradle file:

implementation 'com.google.android.filament:filament-android:1.7.0'
implementation 'com.google.android.filament:filament-utils-android:1.7.0'
implementation 'com.google.android.filament:gltfio-android:1.7.0'

I have a model copying to the assets folder as I've seen in their Kotlin example: https://github.com/google/filament/tree/main/android/samples/sample-gltf-viewer

This is my Java attempt at loading a custom GLTF 2.0 model exported from Blender:

import android.content.Context;
import androidx.constraintlayout.widget.ConstraintLayout;

import android.view.Choreographer;
import android.view.SurfaceView;

import com.google.android.filament.Engine;
import com.google.android.filament.utils.*;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public class Model implements Choreographer.FrameCallback {

    static {
        Utils.INSTANCE.init();
    }

    private final String TAG = Model.class.getSimpleName();
    private Context context;
    private SurfaceView surfaceView;
    private ModelViewer modelViewer;
    private Engine engine;
    private Choreographer choreographer;
    private final String modelFilename = "models/modelTest.glb";

    @Override
    public void doFrame(long currentTime) {
        choreographer.postFrameCallback(this);
        modelViewer.render(currentTime);
    }

    public Model(Context context, ConstraintLayout layout) {

        this.context = context;
        choreographer = Choreographer.getInstance();

        surfaceView = new SurfaceView(context);
        layout.addView(surfaceView);
        engine = Engine.create();
        modelViewer = new ModelViewer(surfaceView, engine, null);
        loadModel(modelFilename);
        choreographer.postFrameCallback(this);
    }

    private void loadModel(String filepath) {
        try {
            InputStream buffer = context.getAssets().open(filepath);
            byte[] bytes = new byte[buffer.available()];
            buffer.read(bytes);
            ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
            modelViewer.loadModelGlb(byteBuffer);
            modelViewer.transformToUnitCube(new Float3(0,0,0));
        }
        catch(IOException e) {

        }
    }

}

Edit 1:

This is what displays when using any version above 1.9.4. The choreographer loop makes the static portions look like static noise on a TV. The red parts appear to be a small portion of the model. enter image description here


Solution

  • I fixed this a while back by making a copy of the ModelViewer class and making a custom ModelViewer3D class with a new constructor, which was a nice solution, because it gives you more control over the options than you would normally with the given ModelViewer.

    The uiHelper isOpaque field needed to be set to false to fix the above issue, and for a transparent background of the surface view the renderer needed to reset the clear options (which I made a separate function for, shown below).

         constructor(surfaceView: SurfaceView, engine: Engine = Engine.create(), manipulator: Manipulator? = null, isOpaque: Boolean) : this(engine) {
            cameraManipulator = manipulator ?: Manipulator.Builder()
                    .targetPosition(kDefaultObjectPosition.x, kDefaultObjectPosition.y, kDefaultObjectPosition.z)
                    .viewport(surfaceView.width, surfaceView.height)
                    .build(Manipulator.Mode.ORBIT)
    
            this.surfaceView = surfaceView
            gestureDetector = GestureDetector(surfaceView, cameraManipulator)
            displayHelper = DisplayHelper(surfaceView.context)
            uiHelper.isOpaque = isOpaque
            uiHelper.renderCallback = SurfaceCallback()
            uiHelper.attachTo(surfaceView)
            addDetachListener(surfaceView)
        }
    
        fun makeBackgroundTransparent() {
            val options = renderer.clearOptions
            options.clear = true
            renderer.clearOptions = options
        }
    

    I also ended up using this version of the library since it was the latest at the time:

        implementation 'com.google.android.filament:filament-android:1.9.9'
        implementation 'com.google.android.filament:filament-utils-android:1.9.9'
        implementation 'com.google.android.filament:gltfio-android:1.9.9'