Skip to content

Commit 20a2286

Browse files
Thomas Gorisseclaude
andcommitted
fix: restore Entity typealiases, fix SceneView/ARSceneView static ref migration
- Add Entity/EntityInstance/FilamentEntity typealiases to Engine.kt (were in deleted SceneView.kt) - Revert Scene.kt and ARScene.kt surface management back to AndroidView + SurfaceView/TextureView — AndroidExternalSurface was removed in Compose 1.10.5 - Replace all SceneView.createXxx() / ARSceneView.createXxx() static calls with the equivalent free functions from SceneFactories.kt / ARFactories.kt - Add missing import for LightManager.Builder.color(Color) extension in SceneFactories.kt - Fix PlaneRenderer constructor call in ARScene.kt (drop modelLoader param) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 503cd0a commit 20a2286

4 files changed

Lines changed: 201 additions & 118 deletions

File tree

arsceneview/src/main/java/io/github/sceneview/ar/ARScene.kt

Lines changed: 93 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package io.github.sceneview.ar
22

33
import android.content.Context.WINDOW_SERVICE
4+
import android.graphics.PixelFormat
5+
import android.graphics.SurfaceTexture
46
import android.util.Size
57
import android.view.MotionEvent
8+
import android.view.Surface
9+
import android.view.SurfaceHolder
10+
import android.view.SurfaceView
11+
import android.view.TextureView
612
import android.view.WindowManager as AndroidWindowManager
713
import androidx.activity.ComponentActivity
814
import androidx.compose.foundation.background
@@ -16,14 +22,11 @@ import androidx.compose.runtime.remember
1622
import androidx.compose.runtime.snapshotFlow
1723
import androidx.compose.runtime.snapshots.SnapshotStateList
1824
import androidx.compose.runtime.withFrameNanos
19-
import androidx.compose.ui.ExperimentalComposeUiApi
2025
import androidx.compose.ui.Modifier
2126
import androidx.compose.ui.graphics.Color
22-
import androidx.compose.ui.input.pointer.pointerInteropFilter
2327
import androidx.compose.ui.platform.LocalContext
2428
import androidx.compose.ui.platform.LocalInspectionMode
25-
import androidx.compose.ui.viewinterop.AndroidEmbeddedExternalSurface
26-
import androidx.compose.ui.viewinterop.AndroidExternalSurface
29+
import androidx.compose.ui.viewinterop.AndroidView
2730
import androidx.lifecycle.DefaultLifecycleObserver
2831
import androidx.lifecycle.Lifecycle
2932
import androidx.lifecycle.LifecycleOwner
@@ -530,61 +533,96 @@ fun ARScene(
530533
arPlaneRenderer.viewSize = Size(width, height)
531534
}
532535

533-
@OptIn(ExperimentalComposeUiApi::class)
534536
when (surfaceType) {
535-
SurfaceType.Surface -> AndroidExternalSurface(
536-
modifier = modifier.pointerInteropFilter { event -> touchDispatcher(event); true },
537-
isOpaque = isOpaque
538-
) {
539-
onSurface { surface, width, height ->
540-
swapChainRef.set(engine.createSwapChain(surface))
541-
displayHelper.attach(renderer, display)
542-
cameraGestureDetectorRef.set(CameraGestureDetector(
543-
viewHeight = { height },
544-
cameraManipulator = null
545-
))
546-
applyARResize(width, height)
547-
engine.drainFramePipeline()
548-
549-
surface.onChanged { w, h ->
550-
applyARResize(w, h)
551-
engine.drainFramePipeline()
552-
}
553-
surface.onDestroyed {
554-
cameraGestureDetectorRef.set(null)
555-
swapChainRef.getAndSet(null)?.let { runCatching { engine.destroySwapChain(it) } }
556-
engine.flushAndWait()
557-
displayHelper.detach()
537+
SurfaceType.Surface -> AndroidView(
538+
modifier = modifier,
539+
factory = { ctx ->
540+
SurfaceView(ctx).also { sv ->
541+
if (!isOpaque) sv.holder.setFormat(PixelFormat.TRANSLUCENT)
542+
sv.holder.addCallback(object : SurfaceHolder.Callback {
543+
override fun surfaceCreated(holder: SurfaceHolder) {}
544+
545+
override fun surfaceChanged(
546+
holder: SurfaceHolder, format: Int, width: Int, height: Int
547+
) {
548+
if (swapChainRef.get() == null) {
549+
swapChainRef.set(engine.createSwapChain(holder.surface))
550+
displayHelper.attach(renderer, display)
551+
cameraGestureDetectorRef.set(
552+
CameraGestureDetector(
553+
viewHeight = { sv.height },
554+
cameraManipulator = null
555+
)
556+
)
557+
}
558+
applyARResize(width, height)
559+
engine.drainFramePipeline()
560+
}
561+
562+
override fun surfaceDestroyed(holder: SurfaceHolder) {
563+
cameraGestureDetectorRef.set(null)
564+
swapChainRef.getAndSet(null)?.let {
565+
runCatching { engine.destroySwapChain(it) }
566+
}
567+
engine.flushAndWait()
568+
displayHelper.detach()
569+
}
570+
})
571+
sv.setOnTouchListener { _, event -> touchDispatcher(event); true }
558572
}
559-
}
560-
}
573+
},
574+
update = {}
575+
)
561576

562-
SurfaceType.TextureSurface -> AndroidEmbeddedExternalSurface(
563-
modifier = modifier.pointerInteropFilter { event -> touchDispatcher(event); true },
564-
isOpaque = isOpaque
565-
) {
566-
onSurface { surface, width, height ->
567-
swapChainRef.set(engine.createSwapChain(surface))
568-
displayHelper.attach(renderer, display)
569-
cameraGestureDetectorRef.set(CameraGestureDetector(
570-
viewHeight = { height },
571-
cameraManipulator = null
572-
))
573-
applyARResize(width, height)
574-
engine.drainFramePipeline()
575-
576-
surface.onChanged { w, h ->
577-
applyARResize(w, h)
578-
engine.drainFramePipeline()
579-
}
580-
surface.onDestroyed {
581-
cameraGestureDetectorRef.set(null)
582-
swapChainRef.getAndSet(null)?.let { runCatching { engine.destroySwapChain(it) } }
583-
engine.flushAndWait()
584-
displayHelper.detach()
577+
SurfaceType.TextureSurface -> AndroidView(
578+
modifier = modifier,
579+
factory = { ctx ->
580+
TextureView(ctx).also { tv ->
581+
tv.isOpaque = isOpaque
582+
var textureSurface: Surface? = null
583+
tv.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
584+
override fun onSurfaceTextureAvailable(
585+
st: SurfaceTexture, width: Int, height: Int
586+
) {
587+
textureSurface = Surface(st)
588+
swapChainRef.set(engine.createSwapChain(textureSurface!!))
589+
displayHelper.attach(renderer, display)
590+
cameraGestureDetectorRef.set(
591+
CameraGestureDetector(
592+
viewHeight = { tv.height },
593+
cameraManipulator = null
594+
)
595+
)
596+
applyARResize(width, height)
597+
engine.drainFramePipeline()
598+
}
599+
600+
override fun onSurfaceTextureSizeChanged(
601+
st: SurfaceTexture, width: Int, height: Int
602+
) {
603+
applyARResize(width, height)
604+
engine.drainFramePipeline()
605+
}
606+
607+
override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean {
608+
cameraGestureDetectorRef.set(null)
609+
swapChainRef.getAndSet(null)?.let {
610+
runCatching { engine.destroySwapChain(it) }
611+
}
612+
engine.flushAndWait()
613+
displayHelper.detach()
614+
textureSurface?.release()
615+
textureSurface = null
616+
return true
617+
}
618+
619+
override fun onSurfaceTextureUpdated(st: SurfaceTexture) {}
620+
}
621+
tv.setOnTouchListener { _, event -> touchDispatcher(event); true }
585622
}
586-
}
587-
}
623+
},
624+
update = {}
625+
)
588626
}
589627

590628
// ── DSL content ───────────────────────────────────────────────────────────────────────────────

sceneview/src/main/java/io/github/sceneview/Engine.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import io.github.sceneview.loaders.MaterialLoader
2424
import io.github.sceneview.loaders.ModelLoader
2525
import io.github.sceneview.model.Model
2626

27+
typealias Entity = Int
28+
typealias EntityInstance = Int
29+
typealias FilamentEntity = com.google.android.filament.Entity
30+
typealias FilamentEntityInstance = com.google.android.filament.EntityInstance
31+
2732
fun Engine.createModelLoader(context: Context) = ModelLoader(this, context)
2833
fun Engine.createMaterialLoader(context: Context) = MaterialLoader(this, context)
2934
fun Engine.createEnvironmentLoader(context: Context) = EnvironmentLoader(this, context)

sceneview/src/main/java/io/github/sceneview/Scene.kt

Lines changed: 101 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ package io.github.sceneview
22

33
import android.content.Context
44
import android.content.Context.WINDOW_SERVICE
5+
import android.graphics.PixelFormat
6+
import android.graphics.SurfaceTexture
57
import android.opengl.EGLContext
68
import android.view.MotionEvent
9+
import android.view.Surface
10+
import android.view.SurfaceHolder
11+
import android.view.SurfaceView
12+
import android.view.TextureView
713
import android.view.WindowManager
814
import androidx.activity.ComponentActivity
915
import androidx.compose.foundation.background
@@ -18,14 +24,11 @@ import androidx.compose.runtime.remember
1824
import androidx.compose.runtime.snapshotFlow
1925
import androidx.compose.runtime.snapshots.SnapshotStateList
2026
import androidx.compose.runtime.withFrameNanos
21-
import androidx.compose.ui.ExperimentalComposeUiApi
2227
import androidx.compose.ui.Modifier
2328
import androidx.compose.ui.graphics.Color
24-
import androidx.compose.ui.input.pointer.pointerInteropFilter
2529
import androidx.compose.ui.platform.LocalContext
2630
import androidx.compose.ui.platform.LocalInspectionMode
27-
import androidx.compose.ui.viewinterop.AndroidEmbeddedExternalSurface
28-
import androidx.compose.ui.viewinterop.AndroidExternalSurface
31+
import androidx.compose.ui.viewinterop.AndroidView
2932
import androidx.lifecycle.DefaultLifecycleObserver
3033
import androidx.lifecycle.Lifecycle
3134
import androidx.lifecycle.LifecycleOwner
@@ -311,6 +314,13 @@ fun Scene(
311314
// DisplayHelper for frame pacing — one per composition.
312315
val displayHelper = remember(context) { DisplayHelper(context) }
313316

317+
// Helper to apply a viewport resize.
318+
fun applyResize(width: Int, height: Int) {
319+
view.viewport = Viewport(0, 0, width, height)
320+
cameraManipulator?.setViewport(width, height)
321+
cameraNode.updateProjection()
322+
}
323+
314324
// ── Render loop ───────────────────────────────────────────────────────────────────────────────
315325

316326
LaunchedEffect(engine, renderer, view, scene) {
@@ -350,67 +360,96 @@ fun Scene(
350360
(context.getSystemService(WINDOW_SERVICE) as WindowManager).defaultDisplay
351361
}
352362

353-
fun applyResize(width: Int, height: Int) {
354-
view.viewport = Viewport(0, 0, width, height)
355-
cameraManipulator?.setViewport(width, height)
356-
cameraNode.updateProjection()
357-
}
358-
359-
@OptIn(ExperimentalComposeUiApi::class)
360363
when (surfaceType) {
361-
SurfaceType.Surface -> AndroidExternalSurface(
362-
modifier = modifier.pointerInteropFilter { event -> touchDispatcher(event); true },
363-
isOpaque = isOpaque
364-
) {
365-
onSurface { surface, width, height ->
366-
swapChainRef.set(engine.createSwapChain(surface))
367-
displayHelper.attach(renderer, display)
368-
cameraGestureDetectorRef.set(CameraGestureDetector(
369-
viewHeight = { height },
370-
cameraManipulator = cameraManipulator
371-
))
372-
applyResize(width, height)
373-
engine.drainFramePipeline()
374-
375-
surface.onChanged { w, h ->
376-
applyResize(w, h)
377-
engine.drainFramePipeline()
378-
}
379-
surface.onDestroyed {
380-
cameraGestureDetectorRef.set(null)
381-
swapChainRef.getAndSet(null)?.let { runCatching { engine.destroySwapChain(it) } }
382-
engine.flushAndWait()
383-
displayHelper.detach()
384-
}
385-
}
386-
}
387-
388-
SurfaceType.TextureSurface -> AndroidEmbeddedExternalSurface(
389-
modifier = modifier.pointerInteropFilter { event -> touchDispatcher(event); true },
390-
isOpaque = isOpaque
391-
) {
392-
onSurface { surface, width, height ->
393-
swapChainRef.set(engine.createSwapChain(surface))
394-
displayHelper.attach(renderer, display)
395-
cameraGestureDetectorRef.set(CameraGestureDetector(
396-
viewHeight = { height },
397-
cameraManipulator = cameraManipulator
398-
))
399-
applyResize(width, height)
400-
engine.drainFramePipeline()
401-
402-
surface.onChanged { w, h ->
403-
applyResize(w, h)
404-
engine.drainFramePipeline()
364+
SurfaceType.Surface -> AndroidView(
365+
modifier = modifier,
366+
factory = { ctx ->
367+
SurfaceView(ctx).also { sv ->
368+
if (!isOpaque) sv.holder.setFormat(PixelFormat.TRANSLUCENT)
369+
sv.holder.addCallback(object : SurfaceHolder.Callback {
370+
override fun surfaceCreated(holder: SurfaceHolder) {}
371+
372+
override fun surfaceChanged(
373+
holder: SurfaceHolder, format: Int, width: Int, height: Int
374+
) {
375+
if (swapChainRef.get() == null) {
376+
swapChainRef.set(engine.createSwapChain(holder.surface))
377+
displayHelper.attach(renderer, display)
378+
cameraGestureDetectorRef.set(
379+
CameraGestureDetector(
380+
viewHeight = { sv.height },
381+
cameraManipulator = cameraManipulator
382+
)
383+
)
384+
}
385+
applyResize(width, height)
386+
engine.drainFramePipeline()
387+
}
388+
389+
override fun surfaceDestroyed(holder: SurfaceHolder) {
390+
cameraGestureDetectorRef.set(null)
391+
swapChainRef.getAndSet(null)?.let {
392+
runCatching { engine.destroySwapChain(it) }
393+
}
394+
engine.flushAndWait()
395+
displayHelper.detach()
396+
}
397+
})
398+
sv.setOnTouchListener { _, event -> touchDispatcher(event); true }
405399
}
406-
surface.onDestroyed {
407-
cameraGestureDetectorRef.set(null)
408-
swapChainRef.getAndSet(null)?.let { runCatching { engine.destroySwapChain(it) } }
409-
engine.flushAndWait()
410-
displayHelper.detach()
400+
},
401+
update = {}
402+
)
403+
404+
SurfaceType.TextureSurface -> AndroidView(
405+
modifier = modifier,
406+
factory = { ctx ->
407+
TextureView(ctx).also { tv ->
408+
tv.isOpaque = isOpaque
409+
var textureSurface: Surface? = null
410+
tv.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
411+
override fun onSurfaceTextureAvailable(
412+
st: SurfaceTexture, width: Int, height: Int
413+
) {
414+
textureSurface = Surface(st)
415+
swapChainRef.set(engine.createSwapChain(textureSurface!!))
416+
displayHelper.attach(renderer, display)
417+
cameraGestureDetectorRef.set(
418+
CameraGestureDetector(
419+
viewHeight = { tv.height },
420+
cameraManipulator = cameraManipulator
421+
)
422+
)
423+
applyResize(width, height)
424+
engine.drainFramePipeline()
425+
}
426+
427+
override fun onSurfaceTextureSizeChanged(
428+
st: SurfaceTexture, width: Int, height: Int
429+
) {
430+
applyResize(width, height)
431+
engine.drainFramePipeline()
432+
}
433+
434+
override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean {
435+
cameraGestureDetectorRef.set(null)
436+
swapChainRef.getAndSet(null)?.let {
437+
runCatching { engine.destroySwapChain(it) }
438+
}
439+
engine.flushAndWait()
440+
displayHelper.detach()
441+
textureSurface?.release()
442+
textureSurface = null
443+
return true
444+
}
445+
446+
override fun onSurfaceTextureUpdated(st: SurfaceTexture) {}
447+
}
448+
tv.setOnTouchListener { _, event -> touchDispatcher(event); true }
411449
}
412-
}
413-
}
450+
},
451+
update = {}
452+
)
414453
}
415454

416455
// ── DSL content ───────────────────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)