SpatialBox, and combine Compose UI panels with ISDK-resizable layout panels.SpatialBox componentObject3DSampleIsdk project in Android Studio, and build to your Quest device. When you run the sample, you see a library panel displaying six 3D objects (robot, drone, plant, desk lamp, easy chair, sculpture). Select an object to spawn a physics-enabled instance into the virtual collaboration room. Grab spawned objects with hand tracking or controllers; they scale slightly on hover and maintain physics momentum after release.| File / Scene | What it demonstrates | Key concepts |
|---|---|---|
Main activity with ISDK input listener registration, scene loading, feature setup | AppSystemActivity, IsdkInputListenerSystem, InputListener, PhysicsFeature, GLXFManager | |
Compose UI panel with object spawning logic, spawn animation, SpatialTheme | ComposeViewPanelRegistration, Entity creation, ValueAnimator, OvershootInterpolator | |
Main.scene | Scene composition with library panel, six spawnable objects, and collaboration room environment | Scene hierarchy, Transform, Mesh, Grabbable, Physics, SpatialBox |
scrolling.xml | Layout for ISDK-resizable scroll panels | LayoutXMLPanelRegistration, IsdkPanelResize, ResizeMode |
ResizeMode.Relayout.InputListener via IsdkInputListenerSystem.setInputListener(). The listener filters pointer events by SemanticType.Grab, ignoring non-grab interactions:if (semanticType != SemanticType.Grab.id) return
when (type) {
PointerEventType.Select.id -> { /* grab started */ }
PointerEventType.Unselect.id -> { /* grab ended */ }
}
Object3DSampleIsdkActivity.kt for the full listener implementation. For API details, see InputListener.PhysicsFeature.useGrabbablePhysics = true), this sample sets useGrabbablePhysics = false and manually controls physics state. On Select, the sample saves the original physics state and sets the object to PhysicsState.KINEMATIC, preventing physics simulation during the grab. On Unselect, it restores the saved state to PhysicsState.DYNAMIC, allowing the object to carry momentum from the release motion:// Save state, go KINEMATIC savedState = physics.state physics.state = PhysicsState.KINEMATIC // Restore DYNAMIC on release physics.state = savedState
Object3DSampleIsdkActivity.kt for the state management logic. For PhysicsState values, see Physics reference.SpatialBox component defining collision bounds. The sample computes an inverse scale so the box dimensions remain correct regardless of the object’s scale factor:val invScale = Vector3(1f / scale.x, 1f / scale.y, 1f / scale.z) SpatialBox(min = dimensions * invScale * -1f, max = dimensions * invScale * 1f)
PanelLayout.kt (setUpButton() function) for the box creation logic.setUpButton() function reads mesh URI and scale from a glXF template entity, then creates a new Entity with Mesh, Grabbable, SpatialBox, Scale, Physics, and Transform components. The spawn animation uses ValueAnimator with OvershootInterpolator(1f) and a 1000ms duration, animating the Scale component from zero to the target scale.PanelLayout.kt for the spawning and animation implementation. For grabbable configuration, see Grabbable reference.LayoutXMLPanelRegistration and applies IsdkPanelResize to enable ISDK-based resizing. One panel uses ResizeMode.Relayout; the other uses the default mode. This demonstrates how to add ISDK resize affordances to XML-based panels.Object3DSampleIsdkActivity.kt and scrolling.xml for the panel registration and layout.PhysicsMaterial.WOOD to experiment with different friction and restitution values.