This guide outlines the steps to integrate Spatial Editor projects with Spatial SDK apps. Once integrated, your Spatial Editor project will be located in the scenes directory of your Spatial SDK app:
Set up the Spatial SDK Gradle plugin
The Spatial SDK Gradle plugin is used to process your Spatial Editor project and export it so it can be used by your app. Meta collects telemetry data from the Spatial SDK Gradle Plugin to help improve MPT Products. You can read the Supplemental Meta Platforms Technologies Privacy Policy to learn more.
To get started, ensure you have the following:
Android Studio installed.
A new Android Studio Spatial SDK project created, ideally using a starter sample as a template.
Spatial Editor installed, which includes the Spatial Editor CLI for export tasks.
Next, with your project open in Android Studio:
Open the project’s libs.versions.toml file.
In the [plugins] block, add the Meta Spatial Plugin.
meta-spatial-plugin = { id = "com.meta.spatial.plugin", version.ref = "spatialsdk" }
In app/build.gradle.kts, add the following to the plugins block:
Find the tasks available with the plugin in the Gradle sidebar under the spatial directory. Open the Gradle sidebar by clicking on the Gradle icon in the right sidebar of Android Studio. Android Studio may not configure all Gradle tasks by default. To configure all Gradle tasks so you can see and run them, enable Configure all Gradle tasks during Gradle Sync in File > Settings> Experiemental under the Gradle header. All Gradle tasks can be found under Tasks/spatial.
To execute any of the tasks, either double-click them in the Gradle sidebar or run ./gradlew _task name_.
Export Spatial Editor projects
To prepare your Spatial Editor project for use in your Spatial SDK project, modify the build.Gradle file as follows:
CliPath.set(...): the CLI executable’s location. If not set, the plugin will search predefined locations and may fail if the CLI is not found.
exportItems: Defines a list of projects and asset directories to be exported.
Ensure you replace the placeholder with the correct path to the Spatial Editor executable on your system.
Make custom components available in Spatial Editor
Spatial SDK projects use components for data storage. There are two ways to make custom components available in the Spatial Editor UI.
Define components in XML
Add a component XML file in app/src/main/components. The Spatial Editor will read the XML defined in the folder and display your custom components in UI. If you prefer to use another directory, refer to Specify the custom components folder.
Define components in Kotlin (deprecated)
In Spatial SDK version 0.5.5+, components are defined using XML. However, you may encounter older version that use Kotlin to define components. See the details Connecting Spatial Editor. In those versions, you can make the generateComponents task run automatically after your build by adding this code to app/build.gradle.kts.
You can replace assembleDebug with whichever task you’re using to build your app. This will make it so that the generateComponents task runs anytime you build your app.
Load and change between compositions
The following sample demonstrates how to change between two compositions (Composition1 and Composition2) when a UI button is pressed (button0):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
//load the first composition by name
loadGLXF("Composition1")
}
override fun registerPanels(): List<PanelRegistration> {
return listOf(
LayoutXMLPanelRegistration(
registrationId = R.id.ui_example,
layoutResourceIdCreator = { R.layout.ui_example },
settingsCreator = {
UIPanelSettings(
shape = QuadShapeOptions(width = 2.0f, height = 1.5f),
)
},
panelSetupWithRootView = { rootView, _, _ ->
val button0: Button? = rootView.findViewById<Button>(R.id.button0)
// when the UI button0 is pressed, load Composition1
button0?.setOnClickListener { loadGLXF("Composition1") }
val button1: Button? = rootView.findViewById<Button>(R.id.button1)
// when the UI button1 is pressed, load Composition2
button1?.setOnClickListener { loadGLXF("Composition2") }
}
)
)
}
private fun loadGLXF(compositionName: String): Job {
gltfxEntity?.destroy() // destroy previous gltfx entity if it exists
gltfxEntity = Entity.create()
return activityScope.launch {
glXFManager.inflateGLXF(
Uri.parse("apk:///scenes/${compositionName}.glxf"),
rootEntity = gltfxEntity!!,
keyName = compositionName)
}
}
Target specific objects in a composition with logic
The following example shows how to target a specific node of your composition (environment) with logic (adjusting the visibility):
// wait for GLXF to load before accessing nodes inside it
loadGLXF().invokeOnCompletion {
val composition = glXFManager.getGLXFInfo("example_key_name")
// get the environment entity from the composition
val environmentEntity: Entity? = composition.getNodeByName("Environment").entity
// set the environment to be invisible on start
environmentEntity?.setComponent(Visible(false))
}
private fun loadGLXF(): Job {
gltfxEntity = Entity.create()
return activityScope.launch {
glXFManager.inflateGLXF(
Uri.parse("apk:///scenes/Composition.glxf"),
rootEntity = gltfxEntity!!,
keyName = "example_key_name")
}
}
Populate a panel using Spatial SDK
When populating panels using Spatial SDK, you can differentiate them by setting a unique ID in the Properties panel of Spatial Editor. The ID should reference an ID resource (R.id.panel_name) rather than a layout resource, as the layout is specified separately in the registration.
Here are several methods to layout and define panel content:
Use LayoutXMLPanelRegistration to define panel UI using layout XML.
Use ActivityPanelRegistration to define panel UI using an Activity.
Use IntentPanelRegistration to define panel UI using an Intent (allowing you to pass information along to your inflated Activity).
Use ViewPanelRegistration to define panel UI using a View at runtime.
For example, this script populates a small panel using Jetpack Compose:
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Modifier
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@Composable
fun MyPanel() {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Panel Title", style = MaterialTheme.typography.headlineSmall)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "This is a panel with some content")
}
}