viewer.onSceneInitObservable.add(func) not running

I’ve seen this code in many playgrounds / documentation etc. and so have been using it:

BabylonViewer.viewerManager.getViewerPromiseById("babylon-viewer").then(function(viewer) {
  viewer.onSceneInitObservable.add(onInitFunc);
}

However, I’ve been having issues with the onInitFunc not running, and realised that onSceneInitObservable doesn’t run sometimes. My assumption is that it’s already run by the time I get to adding the function to it.
My current workaround is:

if(viewer.sceneManager && viewer.sceneManager.scene) {
  onInitFunc();
} else {
  viewer.onSceneInitObservable.add(onInitFunc);
}

Is there a better way? Am I using the observable wrong? I can’t just directly call the function because sometimes the scene isn’t ready - seemingly just dependent on network conditions (e.g. onSceneInitObservable seems to almost always be correctly called with a localhost server whereas it fails to call 90% of the time using an actual server). I’m slightly mystified as to why I’m having this issue even though I’ve seen to many examples of it being used exactly like this :sweat_smile:
Thanks!

cc @RaananW

1 Like

The issue may be related to the asynchronous nature of promises.
Here is the small example with all 3 Viewer observables, hope it may help - BabylonJS Viewer Progress Bar with Overlay Image

Thanks for the example! As far as I can tell my code does much the same - the issue seems to be due to the order things run in. In your example we can see that the getViewerPromiseById() always resolves before BabylonJS finishes compiling, meaning that the viewerObservables() function is called and adds the functions to the observables in time. My issue seems to be that the BJS Viewer is ready, with presumably the onSceneInitObservable functions already having been called, before the getViewerPromiseById() resolves. So this is what happens in your example:
image

But in mine (“scene already loaded” is logged in .getViewerPromiseById("babylon-viewer").then(function(viewer) {} right where the function is added to the observable, you can see the BJS Viewer is loaded before that, meaning the functions registered on the observable are never called):
image

This seemingly means any functions registered with the observable are then not run, as the scene init has already happened. It happens mostly on page re-visits, my assumption is that as most of the files are cached by the browser the viewer loads much faster is ready before the .getViewerPromiseById is resolved…

I’m assuming this is maybe intended behaviour for the observer to not call added functions if already loaded (?), but it does create this issue, and I’m hoping there’s a better way of solving it than just hardcoding this check for the sceneManager / scene!

if(viewer.sceneManager && viewer.sceneManager.scene) {
  onInitFunc();
} else {
  viewer.onSceneInitObservable.add(onInitFunc);
}

This is a classic issue with observers - if the observable has already notified, it will never be triggered.

I submitted a solution a long time ago, but some observers were not yet adjusted to use it - Babylon.js/packages/dev/core/src/Misc/observable.ts at master · BabylonJS/Babylon.js (github.com). If the observable has already triggered, it will trigger when a new observer was added. This prevents you from needing this pattern, and is suitable for observables that are technically one-times, like onXXXInit or onXXXDispose. I assign myself and will fix the viewer’s observables to use notifyIfTriggered.

2 Likes

Ah, that makes sense. Thank you! When you do get a chance to fix it it would be amazing if you could ping me so that I know I can update and remove the workaround :sweat_smile:

1 Like

Add notifyIfTriggered to all Init observables by RaananW · Pull Request #15219 · BabylonJS/Babylon.js (github.com)

2 Likes

Much appreciated, thank you for the speed on this fix. I’ve so far been consistently impressed with how good the support here on the forums is, as well as with the overall development of the library - thank you!

2 Likes