0.はじめに
この記事はNTTコノキュー アドベントカレンダー2025の記事です。
こんにちは、NTTコノキューの山本です。
今回はJetpack XRを使ってAndroid XRアプリ向けに3Dモデルを表示させて行こうと思います。
1.開発環境
MacBook Apple M4
Android Studio Narwhal 4 Feature Drop | 2025.1.4 Canary 2
Jetpack XR SDK 1.0.0-alpha07
2.実装
基本的な事はDeveloperのアプリに3Dモデルを追加するに書いてあるのですが
サラッとしか書いていないので実際の実装ベースでご紹介しようと思います。
私が初めて挑戦したのはalpha04の時なのですが、alpha07までで結構変わったので
変更をしっかりと追わないといけないことに注意です。
2.1 モデルの読み込み
GltfModelのcreate()を使用して読み込みを行います。
表示する3Dモデルはglbファイルを使用するのですが
public suspend fun create( session: Session, assetData: ByteArray, assetKey: String, )public suspend fun create(session: Session, path: Path): GltfModel { require(!path.isAbsolute) { "GltfModel.create() expects a path relative to `assets/`, received absolute path $path." } return create(session.platformAdapter, path.toString()) }public suspend fun create(session: Session, uri: Uri): GltfModel = create(session.platformAdapter, uri.toString())
byte array data , path , uri
が利用可能になっています。今回はpathを利用します。

モデルはプロジェクトのapp配下に
assets/modelsディレクトリを作っていただき、そこに配置してください。
val model = GltfModel.create(session, Paths.get("models/YOUR_FILE_NAME"))
2.2 entitiyの作成と制御
実際に使える様にするためにentityのインスタンスを作成します。
val entity = GltfModelEntity.create(session, model)
val modelPosition = Vector3(0f, 0f, 0f)
entity.setPose(Pose(modelPosition))
上記の様にVector3(x,y,z)で初期位置を決めてあげて、setPose()でポジションを指定する事で初期位置の設定ができます。
parent.addChild(entity)
addChildしてあげる事で表示ができます。
entity.startAnimation(loop = true, animationName = animationName)
モデルのアニメーションはstartAnimationでモデルに設定されたアニメーションをStringで指定してあげる事で再生が可能です。
2.3 Composableとして使用できる様にする
さて、entityを作成するだけで3Dモデルの描画自体はできるのですが
アプリのUIとして利用するにはVolumeという物に設定してあげる必要があります。
参考)
Developerのサンプルが例として分かりやすいので引用させていただくと
Subspace {
SpatialPanel(
SubspaceModifier.height(1500.dp).width(1500.dp),
dragPolicy = MovePolicy(),
resizePolicy = ResizePolicy(),
) {
ObjectInAVolume(true)
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Welcome",
fontSize = 50.sp,
)
}
}
}
ObjectInAVolume()
@OptIn(ExperimentalSubspaceVolumeApi::class)
@Composable
fun ObjectInAVolume(show3DObject: Boolean) {
val session = checkNotNull(LocalSession.current)
val scope = rememberCoroutineScope()
if (show3DObject) {
Subspace {
Volume(
modifier = SubspaceModifier
.offset(volumeXOffset, volumeYOffset, volumeZOffset) // Relative position
.scale(1.2f) // Scale to 120% of the size
) { parent ->
scope.launch {
// Load your 3D model here
}
}
}
}
}
ObjectInAVolume()で先ほど紹介したcreate()とentityの作成をして貰って
SpatialPanelの中で通常のComposableの様に呼び出してもらうだけでOKです。
当初はイベントを検知する機能は無かったのですが、アップデートで追加されました。
そちらについては後日ご紹介させていただきます。
表示前、未確認であれば3Dコンテンツが描画可能かどうか
LocalSpatialCapabilities.current.isSpatialUiEnabled
で確認するのをお忘れなく。(これで通常のスマートフォン/XRデバイスでの動作を同一アプリで出しわけできる様になります)
こんな感じでアプリ内に3Dオブジェクトの配置が完了しました。
3.おわりに
今回はアプリで3Dオブジェクトの配置を行うやり方についてご紹介させていただきました。
私は普段Androidエンジニアで、Unityの知識に乏しいのですが
コンテンツさえあればKotlinの知識だけで3Dモデルを扱う事ができるという点がかなり魅力的に感じました。
是非チャレンジしてみてください。
