-
Notifications
You must be signed in to change notification settings - Fork 59
Display a glTF of a bugdroid #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| /* | ||
| * Copyright 2025 The Android Open Source Project | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.example.helloandroidxr.ui.components | ||
|
|
||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.rememberCoroutineScope | ||
| import androidx.compose.ui.platform.LocalContext | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.xr.compose.platform.LocalSession | ||
| import androidx.xr.compose.spatial.Subspace | ||
| import androidx.xr.compose.subspace.Volume | ||
| import androidx.xr.compose.subspace.layout.SubspaceModifier | ||
| import androidx.xr.compose.subspace.layout.offset | ||
| import androidx.xr.scenecore.GltfModel | ||
| import androidx.xr.scenecore.GltfModelEntity | ||
| import com.example.helloandroidxr.R | ||
| import kotlinx.coroutines.guava.await | ||
| import kotlinx.coroutines.launch | ||
| import java.io.InputStream | ||
|
|
||
| @Composable | ||
| fun BugdroidModel(showBugdroid: Boolean) { | ||
| if (showBugdroid) { | ||
| val xrSession = checkNotNull(LocalSession.current) | ||
| val scope = rememberCoroutineScope() | ||
| val context = LocalContext.current | ||
|
|
||
| Subspace { | ||
| val inputStream: InputStream = | ||
| context.resources.openRawResource(R.raw.bugdroid_animated_wave) | ||
| Volume( | ||
| SubspaceModifier.offset(z = 400.dp) // Relative position | ||
| ) { parent -> | ||
| scope.launch { | ||
| val gltfModel = GltfModel.create( | ||
| session = xrSession, | ||
| assetData = inputStream.readBytes(), | ||
| assetKey = "BUGDROID" | ||
| ).await() | ||
| val gltfEntity = GltfModelEntity.create(xrSession, gltfModel) | ||
| // Make this glTF a child of the Volume | ||
| gltfEntity.setParent(parent) | ||
| // Change the size of the large glTF to 10% | ||
| gltfEntity.setScale(0.1f) | ||
| gltfEntity.startAnimation( | ||
| loop = true, | ||
| animationName = "Armature|Take 001|BaseLayer" | ||
| ) | ||
| } | ||
|
Comment on lines
+43
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a couple of concerns with the model loading logic:
Could you consider refactoring this to use // It's generally safer to open and use the InputStream within the scope where it's consumed,
// especially with effects, to ensure it's fresh if the effect re-runs.
Volume(
SubspaceModifier.offset(z = 400.dp) // Relative position
) { parentNode -> // Renamed from 'parent' for clarity
LaunchedEffect(xrSession, parentNode, context) { // Add keys that define when this effect should run
val modelInputStream: InputStream = try {
context.resources.openRawResource(R.raw.bugdroid_animated_wave)
} catch (e: Exception) {
// Log.e("BugdroidModel", "Failed to open resource stream", e)
return@LaunchedEffect // Stop if stream can't be opened
}
try {
val gltfModel = GltfModel.create(
session = xrSession,
assetData = modelInputStream.readBytes(), // readBytes() closes the stream
assetKey = "BUGDROID"
).await()
val gltfEntity = GltfModelEntity.create(xrSession, gltfModel)
// Make this glTF a child of the Volume
gltfEntity.setParent(parentNode)
// Change the size of the large glTF to 10%
gltfEntity.setScale(0.1f)
gltfEntity.startAnimation(
loop = true,
animationName = "Armature|Take 001|BaseLayer"
)
} catch (e: Exception) {
// Log.e("BugdroidModel", "Error loading or displaying glTF model", e)
// Handle or log the exception appropriately
}
}
} |
||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
modifierparameter passed toPrimaryContentis being reused for multiple Composables (Surface,Box, andButton). This is generally not recommended as aModifierinstance should ideally be applied to a single UI element in a composition branch. Reusing it can lead to unexpected layout behaviors or conflicts, especially if the passed-in modifier has properties like size, padding, or click handlers that are not intended for all nested elements.Could you refactor this to apply the incoming
modifierto the root element withinPrimaryContent(e.g., theSurfaceorTextPane) and use newModifierinstances for specific styling of internal elements like theBoxorButton?