sceneview-mcp 3.4.12 → 3.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,787 @@
1
+ /**
2
+ * platform-setup.ts
3
+ *
4
+ * Setup guides for every supported SceneView platform.
5
+ * Consolidates Android, iOS, Web, Flutter, React Native, Desktop, and TV.
6
+ */
7
+ export const PLATFORM_IDS = ["android", "ios", "web", "flutter", "react-native", "desktop", "tv"];
8
+ const ANDROID_3D = `## SceneView Android — 3D Setup
9
+
10
+ ### 1. Gradle Dependencies
11
+
12
+ \`\`\`kotlin
13
+ // build.gradle.kts (app module)
14
+ dependencies {
15
+ implementation("io.github.sceneview:sceneview:3.3.0")
16
+ }
17
+ \`\`\`
18
+
19
+ ### 2. Minimum SDK
20
+
21
+ \`\`\`kotlin
22
+ android {
23
+ defaultConfig {
24
+ minSdk = 24
25
+ }
26
+ compileOptions {
27
+ sourceCompatibility = JavaVersion.VERSION_17
28
+ targetCompatibility = JavaVersion.VERSION_17
29
+ }
30
+ }
31
+ \`\`\`
32
+
33
+ ### 3. Basic 3D Scene
34
+
35
+ \`\`\`kotlin
36
+ @Composable
37
+ fun My3DScreen() {
38
+ val engine = rememberEngine()
39
+ val modelLoader = rememberModelLoader(engine)
40
+ val environmentLoader = rememberEnvironmentLoader(engine)
41
+
42
+ Scene(
43
+ modifier = Modifier.fillMaxSize(),
44
+ engine = engine,
45
+ modelLoader = modelLoader,
46
+ environment = rememberEnvironment(environmentLoader) {
47
+ environmentLoader.createHDREnvironment("environments/sky_2k.hdr")
48
+ ?: createEnvironment(environmentLoader)
49
+ }
50
+ ) {
51
+ rememberModelInstance(modelLoader, "models/chair.glb")?.let { instance ->
52
+ ModelNode(
53
+ modelInstance = instance,
54
+ scaleToUnits = 1.0f,
55
+ centerOrigin = Position(0f, 0f, 0f)
56
+ )
57
+ }
58
+
59
+ LightNode(
60
+ engine = engine,
61
+ type = LightManager.Type.DIRECTIONAL,
62
+ apply = {
63
+ intensity(100_000f)
64
+ castShadows(true)
65
+ }
66
+ )
67
+ }
68
+ }
69
+ \`\`\`
70
+
71
+ ### 4. Asset Location
72
+
73
+ Place 3D assets in \`src/main/assets/\`:
74
+ \`\`\`
75
+ app/src/main/assets/
76
+ models/ # GLB/glTF files
77
+ environments/ # HDR environment maps
78
+ materials/ # Custom .filamat materials
79
+ \`\`\`
80
+
81
+ ### 5. No Manifest Changes Required
82
+
83
+ 3D-only scenes need no special permissions or manifest entries.`;
84
+ const ANDROID_AR = `## SceneView Android — AR Setup
85
+
86
+ ### 1. Gradle Dependencies
87
+
88
+ \`\`\`kotlin
89
+ // build.gradle.kts (app module)
90
+ dependencies {
91
+ implementation("io.github.sceneview:arsceneview:3.3.0")
92
+ // arsceneview includes sceneview transitively
93
+ }
94
+ \`\`\`
95
+
96
+ ### 2. AndroidManifest.xml
97
+
98
+ \`\`\`xml
99
+ <uses-permission android:name="android.permission.CAMERA" />
100
+ <uses-feature android:name="android.hardware.camera.ar" android:required="true" />
101
+
102
+ <application>
103
+ <meta-data android:name="com.google.ar.core" android:value="required" />
104
+ </application>
105
+ \`\`\`
106
+
107
+ ### 3. Camera Permission at Runtime
108
+
109
+ \`\`\`kotlin
110
+ val cameraPermission = rememberLauncherForActivityResult(
111
+ ActivityResultContracts.RequestPermission()
112
+ ) { granted -> if (granted) showAR = true }
113
+
114
+ LaunchedEffect(Unit) {
115
+ cameraPermission.launch(Manifest.permission.CAMERA)
116
+ }
117
+ \`\`\`
118
+
119
+ ### 4. Basic AR Scene
120
+
121
+ \`\`\`kotlin
122
+ @Composable
123
+ fun MyARScreen() {
124
+ val engine = rememberEngine()
125
+ val modelLoader = rememberModelLoader(engine)
126
+ var anchor by remember { mutableStateOf<Anchor?>(null) }
127
+
128
+ val modelInstance = rememberModelInstance(modelLoader, "models/robot.glb")
129
+
130
+ ARScene(
131
+ modifier = Modifier.fillMaxSize(),
132
+ engine = engine,
133
+ modelLoader = modelLoader,
134
+ planeRenderer = true,
135
+ sessionConfiguration = { session, config ->
136
+ config.depthMode =
137
+ if (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC))
138
+ Config.DepthMode.AUTOMATIC else Config.DepthMode.DISABLED
139
+ config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
140
+ config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
141
+ },
142
+ onTouchEvent = { event, hitResult ->
143
+ if (event.action == MotionEvent.ACTION_UP && hitResult != null) {
144
+ anchor = hitResult.createAnchor()
145
+ }
146
+ true
147
+ }
148
+ ) {
149
+ anchor?.let { a ->
150
+ AnchorNode(anchor = a) {
151
+ modelInstance?.let { instance ->
152
+ ModelNode(
153
+ modelInstance = instance,
154
+ scaleToUnits = 0.5f,
155
+ isEditable = true
156
+ )
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ \`\`\`
163
+
164
+ ### 5. Session Configuration Options
165
+
166
+ | Option | Values | Default |
167
+ |--------|--------|---------|
168
+ | \`depthMode\` | \`DISABLED\`, \`AUTOMATIC\` | \`DISABLED\` |
169
+ | \`lightEstimationMode\` | \`DISABLED\`, \`AMBIENT_INTENSITY\`, \`ENVIRONMENTAL_HDR\` | \`ENVIRONMENTAL_HDR\` |
170
+ | \`planeFindingMode\` | \`DISABLED\`, \`HORIZONTAL\`, \`VERTICAL\`, \`HORIZONTAL_AND_VERTICAL\` | \`HORIZONTAL_AND_VERTICAL\` |
171
+ | \`instantPlacementMode\` | \`DISABLED\`, \`LOCAL_Y_UP\` | \`DISABLED\` |
172
+ | \`cloudAnchorMode\` | \`DISABLED\`, \`ENABLED\` | \`DISABLED\` |`;
173
+ const IOS_3D = `## SceneViewSwift — iOS/macOS/visionOS 3D Setup
174
+
175
+ ### 1. SPM Dependency
176
+
177
+ In Xcode: **File > Add Package Dependencies** > paste:
178
+ \`\`\`
179
+ https://github.com/SceneView/sceneview
180
+ \`\`\`
181
+ Set version rule to **"from: 3.3.0"**.
182
+
183
+ Or in Package.swift:
184
+ \`\`\`swift
185
+ // swift-tools-version: 5.10
186
+ import PackageDescription
187
+
188
+ let package = Package(
189
+ name: "MyApp",
190
+ platforms: [.iOS(.v17), .macOS(.v14), .visionOS(.v1)],
191
+ dependencies: [
192
+ .package(url: "https://github.com/SceneView/sceneview", from: "3.3.0")
193
+ ],
194
+ targets: [
195
+ .executableTarget(
196
+ name: "MyApp",
197
+ dependencies: [
198
+ .product(name: "SceneViewSwift", package: "sceneview")
199
+ ]
200
+ )
201
+ ]
202
+ )
203
+ \`\`\`
204
+
205
+ ### 2. Minimum Platform Versions
206
+
207
+ | Platform | Minimum |
208
+ |----------|---------|
209
+ | iOS | 17.0 |
210
+ | macOS | 14.0 |
211
+ | visionOS | 1.0 |
212
+
213
+ ### 3. Basic SwiftUI Integration
214
+
215
+ \`\`\`swift
216
+ import SwiftUI
217
+ import SceneViewSwift
218
+ import RealityKit
219
+
220
+ struct ContentView: View {
221
+ @State private var model: ModelNode?
222
+
223
+ var body: some View {
224
+ SceneView { root in
225
+ if let model {
226
+ root.addChild(model.entity)
227
+ }
228
+ }
229
+ .cameraControls(.orbit)
230
+ .task {
231
+ do {
232
+ model = try await ModelNode.load("models/car.usdz")
233
+ model?.scaleToUnits(1.0)
234
+ } catch {
235
+ print("Failed to load model: \\(error)")
236
+ }
237
+ }
238
+ }
239
+ }
240
+ \`\`\`
241
+
242
+ ### 4. Model Formats
243
+
244
+ | Format | Support |
245
+ |--------|---------|
246
+ | USDZ | Native — recommended for iOS |
247
+ | .reality | Native — RealityKit format |
248
+ | glTF/GLB | Not natively supported (use Android/Web) |`;
249
+ const IOS_AR = `## SceneViewSwift — iOS AR Setup
250
+
251
+ ### 1. SPM Dependency
252
+
253
+ \`\`\`swift
254
+ .package(url: "https://github.com/SceneView/sceneview", from: "3.3.0")
255
+ \`\`\`
256
+
257
+ ### 2. Info.plist — Camera Permission (Required)
258
+
259
+ \`\`\`xml
260
+ <key>NSCameraUsageDescription</key>
261
+ <string>This app uses the camera for augmented reality.</string>
262
+ \`\`\`
263
+
264
+ ### 3. Minimum Platform
265
+
266
+ AR requires **iOS 17.0+**. Not available on macOS or visionOS via \`ARSceneView\`.
267
+
268
+ ### 4. Basic AR Integration
269
+
270
+ \`\`\`swift
271
+ import SwiftUI
272
+ import SceneViewSwift
273
+ import RealityKit
274
+
275
+ struct ARContentView: View {
276
+ @State private var model: ModelNode?
277
+
278
+ var body: some View {
279
+ ARSceneView(
280
+ planeDetection: .horizontal,
281
+ showCoachingOverlay: true,
282
+ onTapOnPlane: { position, arView in
283
+ guard let model else { return }
284
+ let anchor = AnchorNode.world(position: position)
285
+ let clone = model.entity.clone(recursive: true)
286
+ clone.scale = .init(repeating: 0.3)
287
+ anchor.add(clone)
288
+ arView.scene.addAnchor(anchor.entity)
289
+ }
290
+ )
291
+ .edgesIgnoringSafeArea(.all)
292
+ .task {
293
+ model = try? await ModelNode.load("models/robot.usdz")
294
+ }
295
+ }
296
+ }
297
+ \`\`\`
298
+
299
+ ### 5. AR Configuration
300
+
301
+ | Parameter | Options | Default |
302
+ |-----------|---------|---------|
303
+ | \`planeDetection\` | \`.none\`, \`.horizontal\`, \`.vertical\`, \`.both\` | \`.horizontal\` |
304
+ | \`showPlaneOverlay\` | \`true\` / \`false\` | \`true\` |
305
+ | \`showCoachingOverlay\` | \`true\` / \`false\` | \`true\` |`;
306
+ const WEB_3D = `## SceneView Web — Browser 3D Setup
307
+
308
+ SceneView Web uses **Filament.js** — the same rendering engine as Android, compiled to WebAssembly (WebGL2).
309
+
310
+ ### 1. Install
311
+
312
+ \`\`\`bash
313
+ npm install @sceneview/sceneview-web
314
+ \`\`\`
315
+
316
+ Or in a Kotlin/JS Gradle project:
317
+ \`\`\`kotlin
318
+ kotlin {
319
+ js(IR) { browser(); binaries.executable() }
320
+ sourceSets {
321
+ jsMain.dependencies {
322
+ implementation("@sceneview/sceneview-web")
323
+ }
324
+ }
325
+ }
326
+ \`\`\`
327
+
328
+ ### 2. HTML
329
+
330
+ \`\`\`html
331
+ <canvas id="scene-canvas" style="width:100%;height:100vh"></canvas>
332
+ <script src="your-app.js"></script>
333
+ \`\`\`
334
+
335
+ ### 3. Kotlin/JS Code
336
+
337
+ \`\`\`kotlin
338
+ import io.github.sceneview.web.SceneView
339
+ import kotlinx.browser.document
340
+ import org.w3c.dom.HTMLCanvasElement
341
+
342
+ fun main() {
343
+ val canvas = document.getElementById("scene-canvas") as HTMLCanvasElement
344
+ canvas.width = canvas.clientWidth
345
+ canvas.height = canvas.clientHeight
346
+
347
+ SceneView.create(
348
+ canvas = canvas,
349
+ configure = {
350
+ camera {
351
+ eye(0.0, 1.5, 5.0)
352
+ target(0.0, 0.0, 0.0)
353
+ fov(45.0)
354
+ }
355
+ light {
356
+ directional()
357
+ intensity(100_000.0)
358
+ }
359
+ model("models/DamagedHelmet.glb")
360
+ autoRotate()
361
+ },
362
+ onReady = { sceneView ->
363
+ sceneView.startRendering()
364
+ }
365
+ )
366
+ }
367
+ \`\`\`
368
+
369
+ ### 4. Supported Features
370
+
371
+ - Same Filament PBR renderer as Android (WASM)
372
+ - glTF 2.0 / GLB model loading
373
+ - IBL environment lighting (KTX)
374
+ - Orbit camera with mouse/touch/pinch
375
+ - Auto-rotation, directional/point/spot lights
376
+
377
+ ### 5. Limitations
378
+
379
+ - No AR (requires native sensors)
380
+ - WebGL2 required (~95% of browsers)
381
+ - glTF/GLB format only`;
382
+ const FLUTTER_3D = `## SceneView Flutter — 3D Setup
383
+
384
+ SceneView Flutter uses **PlatformView** to embed native SceneView (Android: Filament, iOS: RealityKit).
385
+
386
+ ### 1. Dependencies
387
+
388
+ \`\`\`yaml
389
+ # pubspec.yaml
390
+ dependencies:
391
+ sceneview_flutter: ^3.3.0
392
+ \`\`\`
393
+
394
+ ### 2. Android Setup
395
+
396
+ Add to \`android/app/build.gradle\`:
397
+ \`\`\`groovy
398
+ android {
399
+ defaultConfig {
400
+ minSdkVersion 24
401
+ }
402
+ compileOptions {
403
+ sourceCompatibility JavaVersion.VERSION_17
404
+ targetCompatibility JavaVersion.VERSION_17
405
+ }
406
+ }
407
+ \`\`\`
408
+
409
+ ### 3. iOS Setup
410
+
411
+ In \`ios/Podfile\`:
412
+ \`\`\`ruby
413
+ platform :ios, '17.0'
414
+ \`\`\`
415
+
416
+ ### 4. Basic 3D Scene
417
+
418
+ \`\`\`dart
419
+ import 'package:sceneview_flutter/sceneview_flutter.dart';
420
+
421
+ class My3DScreen extends StatelessWidget {
422
+ @override
423
+ Widget build(BuildContext context) {
424
+ return SceneView(
425
+ modelUrl: 'assets/models/chair.glb', // Android: GLB, iOS: USDZ
426
+ environment: 'assets/environments/sky_2k.hdr',
427
+ cameraOrbit: true,
428
+ scaleToUnits: 1.0,
429
+ onModelLoaded: (controller) {
430
+ print('Model loaded');
431
+ },
432
+ );
433
+ }
434
+ }
435
+ \`\`\`
436
+
437
+ ### 5. Platform Differences
438
+
439
+ | Feature | Android | iOS |
440
+ |---------|---------|-----|
441
+ | Renderer | Filament | RealityKit |
442
+ | Model format | GLB/glTF | USDZ/.reality |
443
+ | Environment | HDR | Built-in IBL |`;
444
+ const FLUTTER_AR = `## SceneView Flutter — AR Setup
445
+
446
+ ### 1. Dependencies
447
+
448
+ \`\`\`yaml
449
+ dependencies:
450
+ sceneview_flutter: ^3.3.0
451
+ \`\`\`
452
+
453
+ ### 2. Android Manifest
454
+
455
+ \`\`\`xml
456
+ <uses-permission android:name="android.permission.CAMERA" />
457
+ <uses-feature android:name="android.hardware.camera.ar" android:required="true" />
458
+ <application>
459
+ <meta-data android:name="com.google.ar.core" android:value="required" />
460
+ </application>
461
+ \`\`\`
462
+
463
+ ### 3. iOS Info.plist
464
+
465
+ \`\`\`xml
466
+ <key>NSCameraUsageDescription</key>
467
+ <string>This app uses the camera for augmented reality.</string>
468
+ \`\`\`
469
+
470
+ ### 4. Basic AR Scene
471
+
472
+ \`\`\`dart
473
+ import 'package:sceneview_flutter/sceneview_flutter.dart';
474
+
475
+ class MyARScreen extends StatelessWidget {
476
+ @override
477
+ Widget build(BuildContext context) {
478
+ return ARSceneView(
479
+ modelUrl: 'assets/models/robot.glb',
480
+ planeDetection: PlaneDetection.horizontal,
481
+ tapToPlace: true,
482
+ scaleToUnits: 0.5,
483
+ onAnchorCreated: (anchor) {
484
+ print('Object placed at: \${anchor.position}');
485
+ },
486
+ );
487
+ }
488
+ }
489
+ \`\`\``;
490
+ const REACT_NATIVE_3D = `## SceneView React Native — 3D Setup
491
+
492
+ SceneView React Native uses **Fabric/Turbo** to bridge to native SceneView.
493
+
494
+ ### 1. Install
495
+
496
+ \`\`\`bash
497
+ npm install @sceneview/react-native
498
+ cd ios && pod install
499
+ \`\`\`
500
+
501
+ ### 2. Android Setup
502
+
503
+ In \`android/app/build.gradle\`:
504
+ \`\`\`groovy
505
+ android {
506
+ defaultConfig {
507
+ minSdkVersion 24
508
+ }
509
+ }
510
+ \`\`\`
511
+
512
+ ### 3. iOS Setup
513
+
514
+ In \`ios/Podfile\`:
515
+ \`\`\`ruby
516
+ platform :ios, '17.0'
517
+ \`\`\`
518
+
519
+ ### 4. Basic 3D Scene
520
+
521
+ \`\`\`tsx
522
+ import { SceneView } from '@sceneview/react-native';
523
+
524
+ export default function My3DScreen() {
525
+ return (
526
+ <SceneView
527
+ style={{ flex: 1 }}
528
+ modelUrl={require('./assets/models/chair.glb')}
529
+ environment={require('./assets/environments/sky_2k.hdr')}
530
+ cameraOrbit={true}
531
+ scaleToUnits={1.0}
532
+ onModelLoaded={() => console.log('Model loaded')}
533
+ />
534
+ );
535
+ }
536
+ \`\`\`
537
+
538
+ ### 5. Platform Differences
539
+
540
+ | Feature | Android | iOS |
541
+ |---------|---------|-----|
542
+ | Renderer | Filament | RealityKit |
543
+ | Model format | GLB/glTF | USDZ/.reality |
544
+ | Bridge | Fabric component | Fabric component |`;
545
+ const REACT_NATIVE_AR = `## SceneView React Native — AR Setup
546
+
547
+ ### 1. Install
548
+
549
+ \`\`\`bash
550
+ npm install @sceneview/react-native
551
+ cd ios && pod install
552
+ \`\`\`
553
+
554
+ ### 2. Android Permissions
555
+
556
+ Add to \`AndroidManifest.xml\`:
557
+ \`\`\`xml
558
+ <uses-permission android:name="android.permission.CAMERA" />
559
+ <uses-feature android:name="android.hardware.camera.ar" android:required="true" />
560
+ <application>
561
+ <meta-data android:name="com.google.ar.core" android:value="required" />
562
+ </application>
563
+ \`\`\`
564
+
565
+ ### 3. iOS Permissions
566
+
567
+ Add to \`Info.plist\`:
568
+ \`\`\`xml
569
+ <key>NSCameraUsageDescription</key>
570
+ <string>This app uses the camera for augmented reality.</string>
571
+ \`\`\`
572
+
573
+ ### 4. Basic AR Scene
574
+
575
+ \`\`\`tsx
576
+ import { ARSceneView } from '@sceneview/react-native';
577
+
578
+ export default function MyARScreen() {
579
+ return (
580
+ <ARSceneView
581
+ style={{ flex: 1 }}
582
+ modelUrl={require('./assets/models/robot.glb')}
583
+ planeDetection="horizontal"
584
+ tapToPlace={true}
585
+ scaleToUnits={0.5}
586
+ onAnchorCreated={(anchor) => console.log('Placed:', anchor)}
587
+ />
588
+ );
589
+ }
590
+ \`\`\``;
591
+ const DESKTOP_3D = `## SceneView Desktop — Setup
592
+
593
+ SceneView Desktop uses **Compose Desktop** with a software 3D renderer (wireframe). A Filament JNI backend is planned for full PBR rendering.
594
+
595
+ ### 1. Gradle Dependencies
596
+
597
+ \`\`\`kotlin
598
+ // build.gradle.kts
599
+ plugins {
600
+ kotlin("jvm")
601
+ id("org.jetbrains.compose")
602
+ }
603
+
604
+ dependencies {
605
+ implementation(compose.desktop.currentOs)
606
+ implementation("io.github.sceneview:sceneview-desktop:3.3.0") // when published
607
+ }
608
+ \`\`\`
609
+
610
+ ### 2. Basic Desktop Scene
611
+
612
+ \`\`\`kotlin
613
+ import androidx.compose.ui.window.Window
614
+ import androidx.compose.ui.window.application
615
+
616
+ fun main() = application {
617
+ Window(
618
+ onCloseRequest = ::exitApplication,
619
+ title = "SceneView Desktop"
620
+ ) {
621
+ // Current: software wireframe renderer
622
+ DesktopScene(
623
+ modifier = Modifier.fillMaxSize(),
624
+ shapes = listOf(
625
+ WireframeCube(position = Float3(0f, 0f, 0f), size = 1f),
626
+ WireframeSphere(position = Float3(2f, 0f, 0f), radius = 0.5f)
627
+ ),
628
+ cameraOrbit = true,
629
+ backgroundColor = Color(0xFF1A1A2E)
630
+ )
631
+ }
632
+ }
633
+ \`\`\`
634
+
635
+ ### 3. Current Status
636
+
637
+ | Feature | Status |
638
+ |---------|--------|
639
+ | Software wireframe renderer | Alpha |
640
+ | Filament JNI (full PBR) | Planned (18-29 day effort) |
641
+ | GLB/glTF loading | Planned (needs Filament JNI) |
642
+ | Windows / macOS / Linux | Alpha |
643
+
644
+ ### 4. Limitations
645
+
646
+ - Currently wireframe only (no PBR, no textures)
647
+ - No AR support (desktop has no camera/sensor API)
648
+ - Model loading requires Filament JNI (not yet available)`;
649
+ const TV_3D = `## SceneView Android TV — Setup
650
+
651
+ SceneView on Android TV uses the same Filament renderer as mobile Android, with D-pad navigation support.
652
+
653
+ ### 1. Gradle Dependencies
654
+
655
+ \`\`\`kotlin
656
+ dependencies {
657
+ implementation("io.github.sceneview:sceneview:3.3.0")
658
+ implementation("androidx.leanback:leanback:1.0.0")
659
+ implementation("androidx.tv:tv-foundation:1.0.0-alpha10")
660
+ }
661
+ \`\`\`
662
+
663
+ ### 2. Manifest
664
+
665
+ \`\`\`xml
666
+ <uses-feature android:name="android.software.leanback" android:required="true" />
667
+ <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
668
+ \`\`\`
669
+
670
+ ### 3. Basic TV Scene with D-pad Controls
671
+
672
+ \`\`\`kotlin
673
+ @Composable
674
+ fun TVModelViewer() {
675
+ val engine = rememberEngine()
676
+ val modelLoader = rememberModelLoader(engine)
677
+ var rotation by remember { mutableFloatStateOf(0f) }
678
+
679
+ // D-pad rotation control
680
+ val focusRequester = remember { FocusRequester() }
681
+ LaunchedEffect(Unit) { focusRequester.requestFocus() }
682
+
683
+ Box(
684
+ modifier = Modifier
685
+ .fillMaxSize()
686
+ .focusRequester(focusRequester)
687
+ .focusable()
688
+ .onKeyEvent { event ->
689
+ when (event.key) {
690
+ Key.DirectionLeft -> { rotation -= 15f; true }
691
+ Key.DirectionRight -> { rotation += 15f; true }
692
+ else -> false
693
+ }
694
+ }
695
+ ) {
696
+ Scene(
697
+ modifier = Modifier.fillMaxSize(),
698
+ engine = engine,
699
+ modelLoader = modelLoader
700
+ ) {
701
+ rememberModelInstance(modelLoader, "models/car.glb")?.let { instance ->
702
+ ModelNode(
703
+ modelInstance = instance,
704
+ scaleToUnits = 2.0f
705
+ )
706
+ }
707
+ }
708
+ }
709
+ }
710
+ \`\`\`
711
+
712
+ ### 4. TV-Specific Considerations
713
+
714
+ - **No touch** — use D-pad for rotation, Select for actions
715
+ - **10-foot UI** — larger models, simpler scenes, bolder text
716
+ - **No AR** — Android TV has no camera/sensors for AR
717
+ - **Performance** — TV SoCs are weaker than phones; limit to <50K triangles`;
718
+ const SETUPS = {
719
+ android: {
720
+ name: "Android (Jetpack Compose)",
721
+ renderer: "Filament (OpenGL ES / Vulkan)",
722
+ status: "Stable (v3.3.0)",
723
+ guide3d: ANDROID_3D,
724
+ guideAr: ANDROID_AR,
725
+ },
726
+ ios: {
727
+ name: "iOS / macOS / visionOS (SwiftUI)",
728
+ renderer: "RealityKit (Metal)",
729
+ status: "Alpha (v3.3.0)",
730
+ guide3d: IOS_3D,
731
+ guideAr: IOS_AR,
732
+ },
733
+ web: {
734
+ name: "Web (Kotlin/JS + Filament.js)",
735
+ renderer: "Filament.js (WebGL2 / WASM)",
736
+ status: "Alpha",
737
+ guide3d: WEB_3D,
738
+ guideAr: null,
739
+ },
740
+ flutter: {
741
+ name: "Flutter (PlatformView bridge)",
742
+ renderer: "Filament (Android) / RealityKit (iOS)",
743
+ status: "Alpha",
744
+ guide3d: FLUTTER_3D,
745
+ guideAr: FLUTTER_AR,
746
+ },
747
+ "react-native": {
748
+ name: "React Native (Fabric bridge)",
749
+ renderer: "Filament (Android) / RealityKit (iOS)",
750
+ status: "Alpha",
751
+ guide3d: REACT_NATIVE_3D,
752
+ guideAr: REACT_NATIVE_AR,
753
+ },
754
+ desktop: {
755
+ name: "Desktop (Compose Desktop)",
756
+ renderer: "Software wireframe (Filament JNI planned)",
757
+ status: "Alpha",
758
+ guide3d: DESKTOP_3D,
759
+ guideAr: null,
760
+ },
761
+ tv: {
762
+ name: "Android TV",
763
+ renderer: "Filament (OpenGL ES)",
764
+ status: "Alpha",
765
+ guide3d: TV_3D,
766
+ guideAr: null,
767
+ },
768
+ };
769
+ export function getPlatformSetup(platform, type) {
770
+ const setup = SETUPS[platform];
771
+ if (!setup) {
772
+ return `Unknown platform "${platform}". Available: ${PLATFORM_IDS.join(", ")}`;
773
+ }
774
+ if (type === "ar" && !setup.guideAr) {
775
+ return `# ${setup.name}\n\n**Status:** ${setup.status}\n**Renderer:** ${setup.renderer}\n\n> AR is not supported on ${setup.name}. Only 3D scenes are available on this platform.\n\nUse \`get_platform_setup\` with \`type: "3d"\` for 3D setup instructions.`;
776
+ }
777
+ const guide = type === "ar" ? setup.guideAr : setup.guide3d;
778
+ return `# ${setup.name}\n\n**Status:** ${setup.status} | **Renderer:** ${setup.renderer}\n\n${guide}`;
779
+ }
780
+ export function listPlatforms() {
781
+ const header = `## SceneView — Supported Platforms\n\n| Platform | Renderer | Status | 3D | AR |\n|----------|----------|--------|-----|-----|\n`;
782
+ const rows = PLATFORM_IDS.map((id) => {
783
+ const s = SETUPS[id];
784
+ return `| ${s.name} | ${s.renderer} | ${s.status} | Yes | ${s.guideAr ? "Yes" : "No"} |`;
785
+ }).join("\n");
786
+ return header + rows + "\n\nUse `get_platform_setup` with a specific platform for full setup instructions.";
787
+ }