SceneView / sceneview-android

SceneView is a 3D and AR Android Composable and View with Google Filament and ARCore. This is a Sceneform replacement in Kotlin
Apache License 2.0
756 stars 151 forks source link

Would it be possible to add a sample for the new ViewNode in Compose (in v2.1.0)? #456

Open wilfredbtan opened 3 months ago

wilfredbtan commented 3 months ago

Previously, I would use the ViewRenderable to render an AndroidView. However, as ViewNode has changed significantly in v2.1.0, I am unable to get my AndroidView to show properly.

This is my old code:

// Dummy AndroidView
 val androidView =
      android.view.View(context)
          .apply {
              measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
              layout(0, 0, 10, 5)
              setBackgroundColor(Color.WHITE)
          }

ViewRenderable.builder()
    .setView(context, androidView)
    .build(engine)
    .thenAccept {
        val viewAttachmentManager = ViewAttachmentManager(context, sceneView)

        viewAttachmentManager.onResume()

        val childNode = ViewNode(
            engine = engine,
            modelLoader = modelLoader,
            viewAttachmentManager = viewAttachmentManager,
        ).apply {
            setRenderable(it)
        }

        childNodes.add(childNode)

Scene(
    modifier = Modifier.fillMaxSize(),
    engine = engine,
    modelLoader = modelLoader,
    cameraNode = cameraNode,
    childNodes = childNodes,
    view = view,
    // ...
)

This is my broken attempt at trying to render the a child node:

val viewNodeWindowManager = rememberViewNodeManager()
val materialLoader = rememberMaterialLoader(engine)

// ...
rememberNode {
    ViewNode(
        engine = engine,
        windowManager = viewNodeWindowManager,
        materialLoader = materialLoader,
        content = {
            Text(text = context.getString("Some text"))
        }
    )
    .also {
        childNodes.add(it)
    }
}

Scene(
    modifier = Modifier.fillMaxSize(),
    engine = engine,
    modelLoader = modelLoader,
    cameraNode = cameraNode,
    childNodes = childNodes,
    view = view,
    // ...
    viewNodeWindowManager = viewNodeWindowManager   // added
)

Running the above results in the following crash:

FATAL EXCEPTION: main
Process: com.wilfredbtan.choreographic, PID: 31606
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{68df235 V.E...... ......I. 0,0-0,0}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:352)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:325)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.LifecycleAware$lambda$0(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.$r8$lambda$FWAPLXs0qWMqekhMr83xkKattCY(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$$ExternalSyntheticLambda0.createRecomposer(D8$$SyntheticClass:0)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:300)
at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:244)
at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:283)
at android.view.View.dispatchAttachedToWindow(View.java:22479)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3686)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3695)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3695)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3669)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3116)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10885)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1301)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309)
at android.view.Choreographer.doCallbacks(Choreographer.java:923)
at android.view.Choreographer.doFrame(Choreographer.java:852)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8757)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Could anyone guide me in the right direction to render a ViewNode properly in v2.1.0?

NikitaGvozdilko commented 2 months ago

The problem is in ViewAttachmentManager. It adds FrameLayout to WindowManager and non of them have reference on LifecycleOwner.

Here is workaround I created temporary. Replace lib's ViewAttachmentManager on this one:

`import android.content.Context import android.graphics.PixelFormat import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.google.ar.sceneform.rendering.ViewAttachmentManager

/**