How can I define Gizmo in React and Babylon JS, give me an initial boilerplate

I’m trying to define a gizmo in React like in the example:
https://playground.babylonjs.com/#31M2AP#6

But I’ve searched the internet and I can’t find an initial boilerplate that shows me this in React, preferably with TypeScript.
Could anyone provide an initial example?

I’m assuming its just something like

var gizmo = new PositionGizmo(utilLayer); but you will need to have a UtilityLayerRenderer and a scene also like in the example.

Are you trying to make something not in the playground?

hi @JRobsonGomes,

Good question. If you are using react-babylonjs then it’s not possible currently declaratively, but I could add something like this:

const GizmoSphere = () => (
  <utilityLayerRenderer>
    <positionGizmo updateGizmoRotationToMatchAttachedMesh={false} updateGizmoPositionToMatchAttachedMesh>
      <sphere segments={16} diameter={2} position-y={1} rotation-z={Math.PI/2} />
    </positionGizmo>
  </utilityLayerRenderer>
)

The gizmo needs a deferred creation to make sure the UtilityLayerRenderer is ready (like ShadowGenerators that need a light source), but otherwise looks like not too much to add. For the sphere you maybe want another declarative way, such as assignTo='attachedMesh' (that is already available on all host elements).

If you aren’t using react-babylonjs then just an imperative way. If you are using babylonjs-hook there is also a useScene hook where you can at least bring in the Scene and imperatively add the playground code. It’s only with a reconciler that you can get a ref (useRef) for attachedMesh, though.

edit: I would maybe prefer the declaration to have the <positionGizmo ..> as a child of the mesh. Anyway, will wait to see what libraries you are using, if any.

Yes @msDestiny14 the way React handles things is different. The documentation is pretty complete when it comes to pure JavaScript, but when working with React it doesn’t have documentation support, hence the question.

Hi @Brianzinn

I understand, but how would this behave in my scenario. I believe I have to make a reference to the scene, but I still can’t develop it. Do I have to have the meshes inside the positionGizmo that is inside the utilityLayerRenderer and that would all have to be inside the scene?
I would like a way that would allow me to add and remove meshes in the scene and these meshes when clicking would bring up the Gizmo. So would each mesh have its own Gizmo or would one have a global Gizmo in the scene that meshes would be added inside? I’m still not sure how things work.

I just added a new feature a couple of days ago where you can declaratively add shadow casters without knowing the mesh names.

<directionalLight
  name="shadow-light"
  setDirectionToTarget={[Vector3.Zero()]}
  direction={Vector3.Zero()}
  position={new Vector3(-40, 30, -40)}
  intensity={0.8}
  shadowMinZ={1}
  shadowMaxZ={2500}
>
  <shadowGenerator
    mapSize={1024}
    useBlurExponentialShadowMap={true}
    blurKernel={32}
    darkness={0.8}
    forceBackFacesOnly={true}
    depthScale={100}
    shadowCastChildren
  >
    <Corpo
      largura={largura / 1000}
      altura={altura / 1000}
      profundidade={profundidade / 1000}
      posX={0.4}
      posY={0}
      posZ={-0.15}
    />
  </shadowGenerator>
</directionalLight>

So, the new custom property is shadowCastChildren, which you can use instead of passing in mesh names:

shadowCasters={["box1", "caixa"]}

Since the position Gizmo should only apply to one mesh and also that you would want multiple Gizmos was why I then suggested that the Gizmo be a child component of the mesh. That would lead to a cleaner solution I think.

If you want to just have the position gizmo then on specific meshes then you would need a way to store that state (like onClick or something). Then you would have something like this:

const GizmoSphere = () => {
  const [showGizmo, setShowGizmo] = useState();
  return (
    <sphere ...>
    {showGizmo &&
      <positionGizmo ... />
    }
    </sphere>
  );
}

That is a proposal, you would still need a utility later renderer below that.

edit: fix typo
edit2: actually that shadowCastChildren feature isn’t on NPM yet. I can publish if you wanted to look.

1 Like

Thanks for letting me know about the shadows feature, it will be very welcome when you can update. :slightly_smiling_face:
But back to Gizmo, it could give a more concrete example. If it’s not asking too much for a basic functional playground to help me get started.

I should have something tomorrow. It looks like the utilityLayer is an optional constructor property and it can be set afterwards. I’m going to add a property/ies to instruct the gizmo if it should attach to a mesh (default) or node. Then that example above should work. I have a couple of other things to work out, since there are a lot of different gizmos.

I am very grateful for your help.

1 Like

ok, so the syntax looks like this:

const GizmoBox = ({position, color} = props) => (
  <box size={2} position={position}>
    <standardMaterial diffuseColor={color} specularColor={Color3.Black()} />
    <positionGizmo />
  </box>
)

export const Gizmo = () => (
  <div style={{ flex: 1, display: 'flex' }}>
    <Engine antialias adaptToDeviceRatio>
      <Scene>
        <freeCamera name='camera1' ... />
        <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
        <utilityLayerRenderer>
          <GizmoBox color={Color3.Red()} position={new Vector3(-2, 0, 0)} />
          <GizmoBox color={Color3.Yellow()} position={new Vector3(2, 0, 0)} />
        </utilityLayerRenderer>
      </Scene>
    </Engine>
  </div>
)

The gizmos will automatically find their UtilityLayerRenderer and mesh to attach to.

Looks like this:

I just published 3.0.20, which also has the declarative shadow casting from above. Appreciate any feedback. If you want to attach to a node instead of a mesh then I will need to add additional custom properties to Gizmo. Maybe attachToMesh or attachToNode to make that option declarative.

2 Likes

It seems that it’s not ready for use yet. There is a weird error coming from here:
Babylon.js/thinEngine.ts at c4e2d4f38119ba679792bcb6af1cd353e5a3ffbf · BabylonJS/Babylon.js (github.com)

thinEngine.js:1609 Uncaught TypeError: Cannot set property 'active' of undefined
    at Engine.push.../node_modules/@babylonjs/core/Engines/thinEngine.js.ThinEngine.disableAttributeByIndex (thinEngine.js:1609)
    at Engine.push.../node_modules/@babylonjs/core/Engines/thinEngine.js.ThinEngine.unbindAllAttributes (thinEngine.js:3238)
    at Engine.push.../node_modules/@babylonjs/core/Engines/thinEngine.js.ThinEngine.wipeCaches (thinEngine.js:2293)
    at Scene.push.../node_modules/@babylonjs/core/scene.js.Scene.dispose (scene.js:3718)
    at UtilityLayerRenderer.push.../node_modules/@babylonjs/core/Rendering/utilityLayerRenderer.js.UtilityLayerRenderer.dispose (utilityLayerRenderer.js:286)

I’ll try to take a look tomorrow, but it looks like the order I am disposing perhaps is causing that error.

edit: this was fixed in 5.0 latest: scene.dispose idempotent · BabylonJS/Babylon.js@1bcc9d2 (github.com)

1 Like

Great solution. I updated the version and it is working. I haven’t tested the shadow features yet, but I trust your dedication.
I’ll keep an eye on the bug fix update mentioned above, for me it hasn’t appeared yet.
Thanks again. :blush:

One thing I noticed is that it behaves strangely when I change the mesh’s pivot. My mesh has the pivot shifted to the lower left corner. I know that working with transformations can be complicated, but in my case it’s necessary. In a door opening, for example, it would be difficult to turn with the pivot in the center.
Is it possible to get around this problem?
See how I’m shifting the pivot:

import { Color3, Matrix, Mesh, Vector3 } from "@babylonjs/core";
import { useEffect, useRef } from "react";
// import { useState } from "react";

type Props = {
  position: Vector3,
  color: Color3
}

const GizmoBox = ({ position, color }: Props) => {
  // const [showGizmo, setShowGizmo] = useState();
  const gizmoBox = useRef<Mesh>();

  const alterararCriacao = (gizmoBox: Mesh) => {
    //Movendo o pivot para o canto 
    const vectorsBox = gizmoBox.getBoundingInfo().boundingBox.vectorsWorld;
    gizmoBox.setPivotMatrix(Matrix.Translation(Number(vectorsBox[1].x - (vectorsBox[0].x)) / 2,
        Number(vectorsBox[1].y - (vectorsBox[0].y)) / 2,
        -Number(vectorsBox[1].z - (vectorsBox[0].z)) / 2), false);

    //Rederiza arestas
    gizmoBox.enableEdgesRendering();
}

  useEffect(() => {
    if (gizmoBox.current === null) {
        return;
    }

}, [gizmoBox]);

  return (
    <box name="box" ref={gizmoBox} size={2} position={position} onCreated={gizmoBox => alterararCriacao(gizmoBox)} >
      <standardMaterial name="mat1" diffuseColor={color} specularColor={Color3.Black()} />
      <positionGizmo planarGizmoEnabled={true} />
    </box>
  );
}

export default GizmoBox;

hi, sorry I didn’t catch that you had a follow up question. can you post a new question? if it has a playground instead of React that is best. I suspect it is something in the Gizmo itself.

1 Like