Shared prefabs / lighting bug

Hey guys,

(apologies in advance as this became a two-in-one question/bug report)

I need a workflow for this kind of use case: Prefab A and prefab B both contain prefab C (in their hierarchy). Both A and B are referenced as AssetRefences through another component which is responsible for instantiating and destroying A and B.

Now the meat of it: Prefab C is pretty heavy-weight so I don’t want both A.glb and B.glb to actually contain C but to reference C.glb.

–
My almost-solution:

I don’t know if it’s an intended use for GltfObject but I can get pretty close to my requirements if I add an GltfObject component to prefab C’s root and keep both instance of C in A and B named equally. Now I get light-weight A.glb and B.glb and a single heavy-weight C.glb.
Instantiating A or B now also automatically loads C.

Problem with that is: If A or B (and thus C) gets destroyed, it messes with the scene lighting.

Hi Krizzl0r, thanks for sharing your issue. The problem you describe seems to be that using shared GLB assets (prefab C) via the GltfObject component causes side effects when one of the parent prefabs (A or B) is destroyed – likely because the shared lights (and possibly other scene data) are being removed along with the instance even though they’re meant to be shared.

A good solution is to decouple the heavy shared asset (prefab C) from the instantiation/destruction lifecycle of A and B. Instead of letting each prefab load and later destroy C, consider loading prefab C just once through its own AssetReference and storing it in a persistent container (or manager). That way, even if A or B is destroyed, the shared resource (and its lighting) remains intact.

One approach is to create a dedicated “SharedPrefabManager” component that loads prefab C and adds it to the scene root. For example:


import { Behaviour, serializable, AssetReference } from "@needle-tools/engine";
import { Object3D } from "three";

export class SharedPrefabManager extends Behaviour {
  @serializable(AssetReference)
  sharedPrefab?: AssetReference;

  private instance?: Object3D;

  async onEnable() {
    if (this.sharedPrefab) {
      // Load prefab C once
      this.instance = await this.sharedPrefab.loadAssetAsync();
      // Add it to a persistent container (e.g. scene root)
      this.context.scene.add(this.instance);
    }
  }
}

With this manager in place, prefabs A and B would no longer have to include prefab C in their hierarchy. Instead, they’d reference it externally if needed. If you must have per-prefab transforms of prefab C, another option is to clone the loaded instance (making shallow copies as necessary) without destroying the source that holds your lights.

In some cases the glTF loader (GltfObject) may remove global lights on destruction if the asset was part of the loaded node hierarchy. Keeping the shared asset separately avoids the “lighting bug” you’ve encountered. You can refer to the documentation and samples on AssetReference usage for more details at needle-engine-docs—component-prefab.ts.md.

If these steps don’t fully address the issue, it might help to supply a minimal reproducible example (e.g. via Needle-engine - Vite Template (duplicated) - StackBlitz) so that the team can further inspect the issue. Let me know if you need further details or clarifications!

1 replies left - Become PRO for more replies.
thread_Uo15An1i49N1DkbHh2FOAJ4B
Reply to this post directly if you need further help (bottom right button)

I uploaded a bug report.

So generally using GltfObject here is indeed the intended approach – and yes, having to name them identically is a nuisance but also correct here.

Could you clarify what “messes with the scene lighting” means here? Does any of these objects contain a skybox, or lighting?

If you get the chance you can try and reproduce it locally with the bug report mentioned above.

Here’s a screen capture of what happens when the prefab is instantiated/destroyed in a loop: Imgur: The magic of the Internet
Destroying it takes the scene lighting with it it seems.

No, it’s just plain meshes. “Include skybox” or what it’s called on the GltfObject is disabled, too.

Thanks for the video! Yeah I think we need to take a look at the report. We want to improve some aspects of how lighting/skybox work in additive scenes either ways, so comes at a good time :slight_smile:

1 Like

That’s good news! And I already have a follow-up question.

I noticed a delay when instantiating a prefab A containing C in that A pops up at least a frame before C does. (I just noticed you can even see it in the gif: A would be the cube, and C would be the gear.)

So if A is loaded via e.g. AssetReference.preload(), does it recursively load C rightaway? Or is loading of nested prefabs triggered afterwards?
I’d prefer being able to await loading of A and all it’s dependencies. So I can be sure to have “ready-to-render” model after e.g. AssetReference.preload().

Hey, sorry for being impatient but any progress in this regard?