@OptIn(SpatialSDKExperimentalSplatAPI::class).SplatFeature and enable experimental splat renderingapk:// URIssetComponent(Splat(uri))ControllerListenerSystemSpatialTheme and ComposeViewPanelRegistrationSplatSample directory in Android Studio. Build the project and deploy to your Meta Quest device using the Run button or adb install. When launched, the sample loads a splat scene with a floating control panel where you can switch between two bundled Gaussian Splat scenes. For detailed build instructions, dependency versions, and troubleshooting, see the sample’s README.| File / Scene | What it demonstrates | Key concepts |
|---|---|---|
Main activity orchestrating splat rendering, feature registration, controller input, and panel interaction | SplatFeature registration, splat entity creation, controller button handling, panel positioning | |
Jetpack Compose control panel for selecting splat scenes | SpatialTheme, dark/light mode support, interactive UI with preview images | |
Build configuration for splat support | meta-spatial-sdk-splat dependency | |
Manifest requirements for experimental features | com.oculus.experimental.enabled feature declaration | |
app/scenes/Composition.glxf | GLXF scene composition for room environment | Environment model, floor, IBL lighting |
SplatFeature alongside other features in registerFeatures(). Notice how the activity opts into the experimental API with the @OptIn annotation at class level, and the feature requires both spatialContext and systemManager to initialize.override fun registerFeatures(): List<SpatialFeature> {
return listOf(VRFeature(this), SplatFeature(this.spatialContext, systemManager), ComposeFeature())
}
@OptIn(SpatialSDKExperimentalSplatAPI::class) to enable the experimental splat API. See SplatSampleActivity.kt for the complete feature registration.Splat, Transform, Scale, and SupportsLocomotion components. Notice the -90 degree rotation on the X axis to align the splat’s coordinate space with the room environment.splatEntity = Entity.create(listOf(
Splat(splatPath),
Transform(Pose(Vector3(0f, 0f, 0f), Quaternion(-90f, 0f, 0f))),
Scale(Vector3(1f)), SupportsLocomotion(),
))
initializeSplat() method in SplatSampleActivity.kt.setComponent() with a new Splat instance pointing to a different URI. The SDK automatically unloads the previous splat and loads the new one. Notice how the sample disables panel interaction during loading to prevent race conditions.setPanelInteractive(false) splatEntity.setComponent(Splat(newSplatPath.toUri()))
SplatLoadEventArgs.EVENT_NAME event fires, signaling the splat has finished loading. See the loadSplat() method and event listener in SplatSampleActivity.kt.ControllerListenerSystem to detect button presses on the right controller. Notice the bitwise operations to filter for ButtonA and ButtonB in the changedButtons and buttonState masks.if ((controller.changedButtons and ButtonBits.ButtonA) != 0 &&
(controller.buttonState and ButtonBits.ButtonA) != 0) {
positionPanelInFrontOfUser(panelOffset)
}
ControllerListenerSystem inner class in SplatSampleActivity.kt for the complete button handling logic.ComposeViewPanelRegistration and SpatialTheme from the UISet library. Notice how the panel supports dark and light modes via isSystemInDarkTheme() and includes Grabbable(type = GrabbableType.PIVOT_Y) to allow users to reposition it.panelEntity = Entity.createPanelEntity(
R.id.control_panel,
Transform(Pose(Vector3(0f, panelHeight, 0f), Quaternion(0f, 180f, 0f))),
Grabbable(type = GrabbableType.PIVOT_Y, minHeight = 0.75f, maxHeight = 2.5f),
)
Splat component accepts a Uri, and NetworkedAssetLoader is initialized with OkHttp in this sample. Experiment with passing network or local file URIs to loadSplat() instead of bundled apk:// resources.SplatControlPanel.kt to display a progress spinner while splats load, using the SplatLoadEventArgs timing.SpatialTheme and Horizon UI components