diff --git a/samples/gltf-camera/.gitignore b/samples/gltf-camera/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/samples/gltf-camera/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/gltf-camera/build.gradle b/samples/gltf-camera/build.gradle new file mode 100644 index 00000000..529f7361 --- /dev/null +++ b/samples/gltf-camera/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +android { + namespace "io.github.sceneview.sample.gltfcamera" + + compileSdk 34 + + defaultConfig { + applicationId "io.github.sceneview.sample.gltfcamera" + minSdk 28 + targetSdk 34 + versionCode 1 + versionName "1.0.0" + } + + buildTypes { + release { + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17 + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.14" + } + androidResources { + noCompress 'filamat', 'ktx' + } +} + +dependencies { + implementation project(":samples:common") + + implementation "androidx.compose.ui:ui:1.6.7" + implementation "androidx.compose.foundation:foundation:1.6.7" + implementation 'androidx.activity:activity-compose:1.9.0' + implementation 'androidx.compose.material:material:1.6.7' + implementation "androidx.compose.ui:ui-tooling-preview:1.6.7" + implementation "androidx.navigation:navigation-compose:2.7.7" + debugImplementation "androidx.compose.ui:ui-tooling:1.6.7" + + // SceneView + releaseImplementation "io.github.sceneview:sceneview:2.2.1" + debugImplementation project(":sceneview") +} diff --git a/samples/gltf-camera/src/main/AndroidManifest.xml b/samples/gltf-camera/src/main/AndroidManifest.xml new file mode 100644 index 00000000..93ea670d --- /dev/null +++ b/samples/gltf-camera/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/gltf-camera/src/main/assets/environments/symmetrical_garden_02_4k.hdr b/samples/gltf-camera/src/main/assets/environments/symmetrical_garden_02_4k.hdr new file mode 100644 index 00000000..ea40cea0 Binary files /dev/null and b/samples/gltf-camera/src/main/assets/environments/symmetrical_garden_02_4k.hdr differ diff --git a/samples/gltf-camera/src/main/assets/models/halls_green_haa_emplacement.glb b/samples/gltf-camera/src/main/assets/models/halls_green_haa_emplacement.glb new file mode 100644 index 00000000..091feae3 Binary files /dev/null and b/samples/gltf-camera/src/main/assets/models/halls_green_haa_emplacement.glb differ diff --git a/samples/gltf-camera/src/main/java/io/github/sceneview/sample/gltfcamera/MainActivity.kt b/samples/gltf-camera/src/main/java/io/github/sceneview/sample/gltfcamera/MainActivity.kt new file mode 100644 index 00000000..28eaffa7 --- /dev/null +++ b/samples/gltf-camera/src/main/java/io/github/sceneview/sample/gltfcamera/MainActivity.kt @@ -0,0 +1,174 @@ +package io.github.sceneview.sample.gltfcamera + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Done +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import io.github.sceneview.Scene +import io.github.sceneview.node.ModelNode +import io.github.sceneview.rememberEngine +import io.github.sceneview.rememberEnvironmentLoader +import io.github.sceneview.rememberModelLoader +import io.github.sceneview.rememberNode +import io.github.sceneview.sample.SceneviewTheme + +private const val kAperture = 16f +private const val kShutterSpeed = 1f / 125f +private const val kSensitivity = 100f + +class MainActivity : ComponentActivity() { + + @OptIn( + ExperimentalMaterial3Api::class + ) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + SceneviewTheme { + Box(modifier = Modifier.fillMaxSize()) { + val engine = rememberEngine() + val modelLoader = rememberModelLoader(engine) + val environmentLoader = rememberEnvironmentLoader(engine) + + val modelNode = rememberNode { + ModelNode( + modelInstance = modelLoader.createModelInstance( + assetFileLocation = "models/halls_green_haa_emplacement.glb" + ) + ) + } + val cameraNodes = remember(modelNode) { + modelNode.cameraNodes.onEach { + it.setExposure(kAperture, kShutterSpeed, kSensitivity) + } + } + + var selectedCameraNode by remember { mutableStateOf(cameraNodes[0]) } + + Scene( + modifier = Modifier + .fillMaxSize() + .onSizeChanged { modelNode.updateCamerasProjection(it) }, + engine = engine, + modelLoader = modelLoader, + cameraNode = selectedCameraNode, + childNodes = listOf(modelNode), + environment = environmentLoader.createHDREnvironment( + assetFileLocation = "environments/symmetrical_garden_02_4k.hdr" + )!! + ) + ChipsGroup( + labels = cameraNodes.map { it.name ?: "" }, + onSelected = { + selectedCameraNode = cameraNodes[it] + } + ) + + TopAppBar( + title = { + Image( + modifier = Modifier + .width(192.dp), + painter = painterResource(id = R.drawable.logo), + contentDescription = "Logo" + ) + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.25f), + titleContentColor = MaterialTheme.colorScheme.onPrimary + + ) + ) + } + } + } + } + + @OptIn(ExperimentalLayoutApi::class) + @Composable + fun BoxScope.ChipsGroup(labels: List, onSelected: (index: Int) -> Unit) { + var selectedIndex by remember { mutableIntStateOf(0) } + FlowRow( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomEnd) + .background(MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f)) + .navigationBarsPadding() + .padding(8.dp) + ) { + + labels.forEachIndexed { index, label -> + val selected = selectedIndex == index + FilterChip( + label = { + Text( + style = MaterialTheme.typography.bodyLarge.copy(), + text = label + ) + }, + modifier = Modifier.padding(4.dp), + selected = selected, + onClick = { + selectedIndex = index + onSelected(index) + }, + leadingIcon = if (selected) { + { + Icon( + imageVector = Icons.Filled.Done, + contentDescription = "Done icon", + modifier = Modifier.size(FilterChipDefaults.IconSize) + ) + } + } else { + null + } + ) + } + } + } +} + +fun ModelNode.updateCamerasProjection(viewPortSize: IntSize) { + cameraNodes.forEach { cameraNode -> + cameraNode.updateProjection( + aspect = viewPortSize.let { it.width.toDouble() / it.height.toDouble() }, + near = 0.05f, + far = 5000.0f + ) + } +} \ No newline at end of file diff --git a/samples/gltf-camera/src/main/res/values/strings.xml b/samples/gltf-camera/src/main/res/values/strings.xml new file mode 100644 index 00000000..f5eadbe6 --- /dev/null +++ b/samples/gltf-camera/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + glTF Camera + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6c1aa257..5f52e5d3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,6 @@ include ':samples:ar-cloud-anchor' include ':samples:ar-model-viewer' include ':samples:ar-model-viewer-compose' include ':samples:ar-point-cloud' +include ':samples:gltf-camera' include ':samples:model-viewer' include ':samples:model-viewer-compose' \ No newline at end of file