ARTICLE AD BOX
I'm using SceneView 2.3.3 in Android Studio (Narwhal 4 Feature Drop | 2025.1.4)
implementation "io.github.sceneview:sceneview:2.3.3"
I have a 3D model in my project in the form of a .glb file from Blender 4.5.6.
This file contains one character (rig and mesh) with three different poses, each animated and consisting of a single frame.
My goal is to display the desired pose in a sceneview.
From my research, it's best to manipulate the sceneview using Kotlin, which is why my Java activity calls a Kotlin function to handle the display.
Here is the Java code for my activity:
package com.smk.monprojet; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import com.google.android.filament.EntityManager; import com.google.android.filament.LightManager; import io.github.sceneview.SceneView; public class ModelViewerActivity extends AppCompatActivity { private SceneView sceneView; private ProgressBar progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_model_viewer); sceneView = findViewById(R.id.sceneView); progress = findViewById(R.id.progress); // --- Éclairage directionnel (inchangé) --- int lightEntity = EntityManager.get().create(); new LightManager.Builder(LightManager.Type.DIRECTIONAL) .color(1.0f, 1.0f, 1.0f) .intensity(100_000.0f) .direction(0f, -1f, -1f) .castShadows(true) .build(sceneView.getEngine(), lightEntity); sceneView.getScene().addEntity(lightEntity); progress.setVisibility(View.VISIBLE); try { String animationNameToLoad = "pose_113"; String assetPathToLoad = "models/Modele3D.glb"; io.github.sceneview.node.ModelNode modelNode = ModelLoaderHelper.displaySinglePose( sceneView, assetPathToLoad, animationNameToLoad ); } catch (Exception e) { e.printStackTrace(); } finally { progress.setVisibility(View.GONE); } } @Override protected void onDestroy() { if (sceneView != null) { sceneView.destroy(); // libère Filament/ressources GPU } super.onDestroy(); } }Before the Try block, the lighting is simply adjusted.
The core functionality is within the Try block: the call to the Kotlin function ModelLoaderHelper.displaySinglePose()
Here is my Kotlin file:
package com.smk.monprojet import android.util.Log import com.google.android.filament.gltfio.FilamentInstance import dev.romainguy.kotlin.math.Float3 import dev.romainguy.kotlin.math.length import dev.romainguy.kotlin.math.lookAt import io.github.sceneview.SceneView import io.github.sceneview.math.Position import io.github.sceneview.math.toFloat3 import kotlin.math.tan object ModelLoaderHelper { @JvmStatic fun displaySinglePose( sceneView: SceneView, assetPath: String, animationName: String ): io.github.sceneview.node.ModelNode { // 1️⃣ Charger l'instance GLB val instance = sceneView.modelLoader.createModelInstance(assetPath) // 2️⃣ Créer le ModelNode val modelNode = io.github.sceneview.node.ModelNode(instance) // 3️⃣ Ajouter à la scène sceneView.addChildNode(modelNode) // 4️⃣ Post pour attendre que le node soit attaché sceneView.post { val animator = instance.animator if (animator != null) { // Cherche l'index de l'animation par son nom var animIndex = -1 for (i in 0 until animator.animationCount) { if (animator.getAnimationName(i) == animationName) { animIndex = i break } } if (animIndex != -1) { // Applique la première frame animator.applyAnimation(animIndex, 0f) animator.updateBoneMatrices() } else { Log.e("SceneViewPose", "Animation '$animationName' introuvable") } } else { Log.e("SceneViewPose", "Animator non disponible sur cette instance") } // 5️⃣ Caméra centrée et reculée val box = modelNode.boundingBox val center = box.center val half = box.halfExtent val centerX = center[0] val centerY = center[1] val centerZ = center[2] val maxHalf = maxOf(half[0], half[1], half[2]) val distance = maxHalf * 2.5f sceneView.cameraNode.position = dev.romainguy.kotlin.math.Float3( centerX, centerY + maxHalf, centerZ + distance ) sceneView.cameraNode.lookAt( targetWorldPosition = dev.romainguy.kotlin.math.Float3(centerX, centerY, centerZ), upDirection = dev.romainguy.kotlin.math.Float3(0f, 1f, 0f), smooth = false, smoothSpeed = 0f ) } return modelNode } }It's not working for two reasons:
The pose isn't displaying, and the model always appears in a T-pose. However, the GLB file does contain all three poses. I've verified this with https://sandbox.babylonjs.com/ and https://gltf-viewer.donmccurdy.com/
The camera refuses to position itself correctly. It's practically at ground level, barely in front of the character (I can see part of their feet in close-up).
