brianzinn / react-babylonjs

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

Get reference to imported model and Babylon methods #317

Closed debashis66 closed 5 months ago

debashis66 commented 7 months ago

Hello

I imported a model using the example

import React, { Suspense } from 'react'; import '@babylonjs/loaders'; import { Vector3 } from 'babylonjs'; import { Engine, Scene, ArcRotateCamera, HemisphericLight, Model } from 'react-babylonjs';

const App = () => (

);

export default App;

I want to get a reference to the imported model (or AbstractMesh) to derive its boundingInfo [using mesh.getBoundingInfo()]. Also any example to use other methods such to setAbsolutePosition, addRotation on the mesh ?

This will allow me to transform the imported model (reposition, rotate) using Babylon API. I can do this with the imperative approach, but how to access these methods using the declarative approach

brianzinn commented 7 months ago

You have options to get a reference to the model. I don't think "onCreated" callback will work. There is a forward ref on <Model ../> and I modified the original example to use that (https://github.com/brianzinn/react-babylonjs/blob/master/packages/static/content/examples/models/look-at/LookAt.tsx#L14) so you can:

const LookAtModel: FC<LookAtModelProps> = ({ lookAtPosition, position, id }) => {
  let baseUrl = 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/'

  const modelRef = useRef<AbstractMesh | null>(null)

  const onModelLoaded = (model: ILoadedModel) => {
    console.log('model loaded:', model);
    model.rootMesh!.lookAt(lookAtPosition.clone())
  }

  useEffect(() => {
    console.log('current:', modelRef.current);
    if (modelRef.current) {
      modelRef.current.lookAt(lookAtPosition)
    }
  }, [lookAtPosition])

  return (
    <Suspense fallback={<box name="fallback" position={position} />}>
      <Model
        ref={modelRef}
        name="Avocado"
        rootUrl={`${baseUrl}Avocado/glTF/`}
        sceneFilename={`Avocado.gltf?id=${id}`}
        scaleToDimension={1}
        position={position}
        onModelLoaded={onModelLoaded}
      />
    </Suspense>
  )
}

On a mesh, you can call methods declaratively (ie: https://github.com/brianzinn/react-babylonjs/blob/master/packages/static/content/examples/ScaledModelWithProgress.tsx#L38), such as like this:

<box
        key="progress"
        setPivotMatrix={[Matrix.Translation(-props.scaleTo, 0, 0)]}
        setPreTransformMatrix={[Matrix.Translation(-props.scaleTo / 2, 0, 0)]}
      >
      ...
 </box>

That doesn't translate well with Model, due to async nature. You can try it though using that syntax. The array will be spread out and passed as args. I think you can pass in an object with props matching argument names and that will work as well. Keep in mind that "addRotation" will be called every time a render happens if you don't memoize the method/callback.

Also, you don't need to import ArcRotateCamera or HemisphericLight. Those are host elements, so you can remove those imports and just do <arcRotateCamera .../> and it will just work. You do need to import Engine, Scene, Model and only a couple of other components, since they are not host elements.

edit: You may find it easier to use a hook directly as well and skip using <Model ../>. There is not much going on in that component https://github.com/brianzinn/react-babylonjs/blob/master/packages/react-babylonjs/src/customComponents/Model.tsx

brianzinn commented 5 months ago

closing from inactivity. if above doesn't help or you have more questions feel free to re-open.