brianzinn / react-babylonjs

React for Babylon 3D engine
https://brianzinn.github.io/react-babylonjs/
812 stars 102 forks source link

Props on <Scene /> not working on load #196

Closed MelMacaluso closed 2 years ago

MelMacaluso commented 2 years ago

Hi there

Thanks for the wrapper!

I've been struggling in making the props work on load.

Any prop I try on the component doesn't work on load, but if I edit them and hit save, they will suddenly start working as expected.

I've been so far using this simple code:

import { useRef } from 'react';
import { Engine, Scene } from 'react-babylonjs';
import { Color4, Constants } from '@babylonjs/core';
import { Vector3 } from '@babylonjs/core/Maths/math';
import '@babylonjs/loaders/glTF';

import ModelViewer from '../ModelViewer';

const ModelViewerScene = ({ model }) => {
  const glowRef = useRef(null);

  return (
    <Engine antialias adaptToDeviceRatio canvasId="nft-card-model">
      <Scene
        clearColor={new Color4(0, 0, 1, 0)}
        onSceneCreated={() => alert('test 1')}
        sceneOptions={{
          onSceneCreated: () => alert('test 2'),
        }}
      >
        <arcRotateCamera
          name="Camera"
          alpha={1.5}
          beta={1.5}
          radius={10}
          lowerRadiusLimit={20}
          upperRadiusLimit={80}
          target={Vector3.Zero()}
          useAutoRotationBehavior
        />
        <hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
        <glowLayer
          ref={glowRef}
          name="glow"
          options={{
            mainTextureSamples: 2,
            alphaBlendingMode: Constants.ALPHA_ONEONE_ONEONE,
          }}
          isEnabled={true}
        />
        <ModelViewer sceneFilename={model} />
      </Scene>
    </Engine>
  );
};
brianzinn commented 2 years ago

Callback should be like this:

<Scene onSceneMount={() => alert('test 1')} ... />

The scene options doesn't have that callback - it may also be split as independent props - there is a big update coming to docs soon to make this more apparent (here is the babylon API doc on sceneOptions for your reference): https://doc.babylonjs.com/typedoc/interfaces/babylon.sceneoptions

Sorry for the poor documentation - hope to have a much improved one up in a week or so.

MelMacaluso commented 2 years ago

Hi @brianzinn , thanks for the swift answer!

It is mainly about the "reactiveness" of the props, in this case, all I wanted to do is instantiate the with a transparent bg color being Color4(0,0,0,0) (it works just if I edit the file and press save, so it starts with an opaque color then it goes transparent after saving....).

The way I "fixed" it, which is not fixed as it blinks for 1 second before being transparent is setting the clearColor prop on the <Scene /> as a useState() variable and setting it on render with useEffect(), not ideal.

Like this:

...
const ModelViewerScene = ({ model }) => {
  const [clearColor, setClearColor] = useState(null);
  const glowRef = useRef(null);

  useEffect(() => {
    setClearColor(new Color4(0, 0, 0, 0.01));
  }, []);

  return (
    <Engine antialias adaptToDeviceRatio canvasId="nft-card-model">
      <Scene clearColor={clearColor}>
...

Is there a way to avoid that and set it right away without the hack?

brianzinn commented 2 years ago

Does this not work?

// this will create a color4 every React render - usually OK or you can useRef, etc..
<Scene clearColor={new Color4(0, 0, 0, 0.1)}>
 ...

or

const ModelViewerScene = ({model, clearColor}) => {
...

  <Scene clearColor={clearColor}>
  ...

When you useEffect like that you are at the mercy of the React scheduler in terms of timing. Keep asking questions!

MelMacaluso commented 2 years ago

Does this not work?

// this will create a color4 every React render - usually OK or you can useRef, etc..
<Scene clearColor={new Color4(0, 0, 0, 0.1)}>
 ...

or

const ModelViewerScene = ({model, clearColor}) => {
...

  <Scene clearColor={clearColor}>
  ...

When you useEffect like that you are at the mercy of the React scheduler in terms of timing. Keep asking questions!

No no, this one does not work:

// this will create a color4 every React render - usually OK or you can useRef, etc..
<Scene clearColor={new Color4(0, 0, 0, 0.1)}>
 ...

Basically anything I assign to as prop is not being applied on first render, I need to manually save the file. That's why I had the intuition to set props with a useEffect() which worked but is a little hack (as you mentioned too).

Ideally I'd like to set it as you mention above.

Does it work for you?

MelMacaluso commented 2 years ago

Just tried onSceneMount prop, this one works on first render (as in triggers the function I assign it to), but it does not set the clear color (bg is still opaque:

  const handleInitScene = (scene) => {
    scene.clearColor = new Color4(0, 0, 0, 0.001);
  };

...

 <Scene onSceneMount={handleInitScene}>
...
MelMacaluso commented 2 years ago

Ok, if I actually cared to check what parameters onSceneMount provided me I would have known that is an object with inside the scene...

  const handleInitScene = ({scene}) => {
    scene.clearColor = new Color4(0, 0, 0, 0.001);
  };

For now this settles it, thanks so much for your help.

Might need to check thought why props like clearColor are not working on first render?

brianzinn commented 2 years ago

Yes, let me check that. I can actually write a unit test for that. I will get back to you as it should work. Cheers.

MelMacaluso commented 2 years ago

Thank you!

brianzinn commented 2 years ago

sorry - i got a bit swamped. for sure tonight...

brianzinn commented 2 years ago

ok - from all of my testing it will be set and the change should be there. This always outputs the scene clear color from the prop:

      <Scene
        clearColor={new Color4(0.2, 0.4, 0.75, 1.0)}
        onSceneMount={({scene}) => console.log('clear color from scene:', scene.clearColor)}
      >
      ...
      </Scene>

Is it possible there is something in your <ModelViewer ../> that is altering the scene clearColor? Maybe a skybox/environment or something?

brianzinn commented 2 years ago

closing for inactivity - please re-open if your question isn't answered.

domenicomanna commented 1 year ago

Still looks like the scene clearColor is not getting set consistently when using the prop. Sometimes after refreshing the clearColor is correct and other times it is not. Code sandbox link

2022-09-08_11:53:19

brianzinn commented 1 year ago

looks related to <StrictMode>... that is why it shows twice as well. I think it was actually fine before React 18... I think this is a separate issue - there are a couple of open ones now related.