RxJS.Observable?

Hi all!

Are there any plans to replace Babylon.Obervable by RxJS.Observable?

  • adhere to a industry standard
  • use cool RxJS features (delay, debounce, map,…)
1 Like

Hey!

no no plan :slight_smile: We do not take external dependencies

But it should be straightforward from a user standpoint to wrap them

2 Likes

For anyone wandering how to wrap BABYLON observable into RxJS one, here is example:

export interface BeforeRenderEvent {
  data: Scene;
  state: EventState
}

export function makeBeforeRenderObservable(scene: Scene): Observable<BeforeRenderEvent> {
  return new Observable<BeforeRenderEvent>((subscriber) => {
    const notifier = scene.onBeforeRenderObservable.add((data: Scene, state: EventState) => {
      subscriber.next({data, state});
    });
    return () => scene.onBeforeRenderObservable.remove(notifier);
  });
}

then in your code you can use it like

makeBeforeRenderObservable(scene).pipe(
    debounceTime(1000),
    tap((event) => console.log('before render evt', event)),
    // ... do rx magic
)
5 Likes

Thanks for this example. I’m just starting out with rxjs. Could you perhaps post an example of wrapping something more deeply nested, like the controller ‘squeeze’ event from this playground example https://playground.babylonjs.com/#B922X8#19

I’m interested in seeing how to port a VR style “drag-and-drop”, or pickup object and throw it, into an rxjs observable style. I struggle with understanding how to make a hot observable out of motion controller data, especially when there are multiple levels of babylon observables. I’m not sure where to start.

@Deltakosh while it’s understandable you want to avoid external dependencies, the kind of observables implemented by RxJS is the one that’s probably going to be included in browsers in a couple of years (Observable). But since that is future talk, I would still really appreciate a small chapter in the documentation on wrapping Babylon observables, i.e. not much more than just the bit @cybedred posted, just to make this more discoverable. Thanks!

That’s a great suggestion! The documentation site is an open source project as well, and we are always happy for contributions! Even creating the ticket for us to be able to track will be appreciated.

Done: Document wrapping BabylonJS observables to other types of observables · Issue #140 · BabylonJS/Documentation · GitHub

2 Likes

By making it generic we only need one wrap.

import { Observable as BJSObservable } from '@babylonjs/core/Misc/observable'
import { Observable } from 'rxjs'

/**
 * Wraps a Babylon Observable into a rxjs Observable
 * @param bjsObservable The Babylon Observable you want to observe
 * @example
 * import { Engine, Scene, AbstractMesh } from '@babylonjs/core'
 * const canvas = document.getElementById('canvas') as HTMLCanvasElement
 * const engine = new Engine(canvas)
 * const scene = new Scene(engine)
 * const render$: Observable<Scene> = fromBabylonObservable(scene.onAfterRenderObservable)
 * const onMeshAdded$: Observable<AbstractMesh> = fromBabylonObservable(scene.onNewMeshAddedObservable)
 */
export function fromBabylonObservable<T>(bjsObservable: BJSObservable<T>): Observable<T> {
  return new Observable<T>((subscriber) => {
    if (!(bjsObservable instanceof BJSObservable)) {
      throw new TypeError('the object passed in must be a Babylon Observable')
    }
    const handler = bjsObservable.add(subscriber.next)
    // When we teardown, we remove the handler
    return () => bjsObservable.remove(handler)
  })
}

5 Likes

Opened a PR to document this. @Leon mostly took your code, with one notable exception:

const handler = bjsObservable.add((v) => subscriber.next(v));

Without explicitly passing the value, it didn’t work for my setup. Luckily @typescript-eslint/unbound-method had me covered :wink:

Gotta say, so far I’m extremely happy with managing state in rxjs :relieved: