Is it possible to define the function

Is it possible to define the hello function

function onSceneReady(scene: Scene) {
	const box = BABYLON.MeshBuilder.CreateBox("box", {size: 2}, scene);
	box.position.z = 3;
 }

export default class Viewer extends Component {

   render() {
    return (
        <Engine antialias adaptToDeviceRatio width={window.innerWidth} height={window.innerHeight}>
            <Scene>
                <arcRotateCamera name='camera1' alpha={Math.PI / 2} beta={Math.PI / 4} radius={2.0} target={new Vector3(0, 0, 0)} />
                <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
					{onSceneReady}
            </Scene>
        </Engine>
    )
  }

}

1 Like

I’m not sure what you mean by “the hello function”. I’m assuming you are using react-babylonJS? @brianzinn may be able to help if the question is more clear. :slight_smile:

2 Likes

The renderer will complain when you try to have {onSceneReady} declared like that (you should see an error in the console).

Yes, you can call a function when the scene is ready, though! :smiley:

import { MeshBuilder } from '@babylonjs/core';
import { Scene, Engine, SceneEventArgs } from 'react-babylonjs';

function onSceneReady(e: SceneEventArgs) {
  const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
  box.position.z = 3;
};

class YourComponent extends React.Component {
  render() {
    return (
        <Engine antialias adaptToDeviceRatio width={window.innerWidth} height={window.innerHeight}>
            <Scene onSceneMount={onSceneReady}>
                <arcRotateCamera name='camera1' alpha={Math.PI / 2} beta={Math.PI / 4} radius={2.0} target={new Vector3(0, 0, 0)} />
                <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
            </Scene>
        </Engine>
    )
}
  1. I would recommend to switch all of your packages to @babylonjs/core, etc (the ES6 ones)
  2. You can use <box position={new Vector3(0, 0, 2)} size={2} /> for declarative, but I suspect you are experimenting in another way.
  3. There is a useScene hook that you can use inside of the <Scene ../> component, if you want the scene available inside your component tree.

Keep asking questions - it’s the best way to learn!

1 Like

I could not complete more than one function

 import React, { Component } from 'react';
import { Scene, Engine,  Box, Sphere, SceneEventArgs, useScene} from 'react-babylonjs';
import { Vector3, MeshBuilder, FreeCamera, HemisphericLight ,Color3} from '@babylonjs/core';

function onSphere(e: SceneEventArgs) {
		const box = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
		box.position.y = 1;
		
	 }



function onSceneReady(e: SceneEventArgs) {
		const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
		
			
	 }

export default class Viewer extends Component {

       render() {
        return (
            <Engine antialias adaptToDeviceRatio width={window.innerWidth} height={window.innerHeight}>
                <Scene  onSceneMount={onSphere} onSceneMount={onSceneReady}>
				
                    <arcRotateCamera name='camera1' alpha={Math.PI / 2} beta={Math.PI / 4} radius={7.0} target={new Vector3(0, 0, 0)} />
                    <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
						
                </Scene>
            </Engine>
        )
	 }
}

My 2nd problem

Viewed.js

import React, { Component } from 'react';
import { Scene, Engine,  Box, Sphere, SceneEventArgs, useScene} from 'react-babylonjs';
import { Vector3, MeshBuilder, FreeCamera, HemisphericLight ,Color3} from '@babylonjs/core';

import Sphere from './sphere':

function onSphere(e: SceneEventArgs) {
		const box = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
		box.position.y = 1;
		
	 }


function onSceneReady(e: SceneEventArgs) {
		const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
		
			
	 }

export default class Viewer extends Component {

       render() {
        return (
            <Engine antialias adaptToDeviceRatio width={window.innerWidth} height={window.innerHeight}>
                <Scene  onSceneMount={onSphere} onSceneMount={onSceneReady}>
				
                    <arcRotateCamera name='camera1' alpha={Math.PI / 2} beta={Math.PI / 4} radius={7.0} target={new Vector3(0, 0, 0)} />
                    <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
					<Sphere />	
                </Scene>
            </Engine>
        )
	 }
}

Sphere.js

import React, { Component } from 'react';
import { Scene, Engine, SphereCylinder,SceneEventArgs, useScene} from 'react-babylonjs';
import { MeshBuilder} from '@babylonjs/core';


function onSphere(e: SceneEventArgs) {
		const box = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
		box.position.y = 1;
	 }


export default class Sphere extends Component {

       render() {
        return (  
                <Scene onSceneMount={onSphere}>	     
        )
	 }
}

I could not solve the problem
thank you in advance for help

hi @3dObject ,

I don’t really know what the reconciler would do when you send the same prop twice. You could get around that by having a single function that calls other functions:

function createBox(e: SceneEventArgs) {
   const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
}

function createSphere(e: SceneEventArgs) {
  const sphere = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
  sphere.position.y = 1;
}

function function onSceneReady(e: SceneEventArgs) {
   createBox(e);
   createSphere(e);
}

....
  <Scene onSceneMount={onSceneMount} />

I would probably discourage using the onSceneMount that way for a few reasons:

  1. You are not creating your scene graph in a way that you can compose your scene and track your objects in components.
  2. If you are building your project that way you don’t get any of the benefits of react-babylonjs with declarative scene composition or re-usable components.

Also, you have a nested <Scene /> in your Sphere component. Try to think of the Scene as typically you have 1 engine and 1 scene. There are special cases where you would have multiple scenes. You can access the babylon Scene object anywhere in the component tree with the hook useScene.

Your Sphere component could just be:

export default class MySphere extends Component {
 render(props) {
    return (
          <sphere diameter={props.diameter} />
    )
  }
}

You can pass now diameter to your MySphere class.

    <Scene .... >
      ... lights/camera/etc.
      <MySphere diameter={2} />
    </Scene>

It’s a lot of concepts to be connecting React and how Babylonjs works. Feel free to ask more questions or provide a more concrete example - it’s hard to see in trivial examples what you are trying to accomplish. The repo main readme has some composition examples, but isn’t using classes extending React.Component, but instead functional components.

1 Like

Hello @brianzinn
How can I complete MyBox here

        function MyBox(e: SceneEventArgs) {
    		const box = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
    		box.position.y = 3;
    	 }

        export default class MySphere extends Component {
         render(props) {
            return (
                  <>
                  MyBox(e);
                  <sphere diameter={props.diameter} />
                 </>
            )
          }
        }

hi @3dObject,

Here is how I would do that in a react Component using a react Fragment to combine shapes:

export default class MyShapes extends Component {
         render(props) {
            return (
                  <>
                  <box name='box' size={2} />
                  <sphere diameter={props.diameter} />
                 </>
            )
          }
        }

If you can show a more real life example then I could maybe work out where you are getting caught up. If you really want to use the MeshBuilder, you could try something like this :crossed_fingers: (just free-typing and untested):

import React, { useState } from 'react';
import { useScene } from 'react-babylonjs';

const MyBox = ({positionY}) => {
  const scene = useScene();
  const box = useState(() => {
     const box = MeshBuilder.CreateBox("box", {size: 2}, scene);
     box.position.y = positionY;
  });

  // at this point you can return null, but if you want access to it again:
  // disposeInstanceOnUnmount will call dispose on the mesh, it's an opt-in feature
  // otherwise you would want to use a useEffect hook or otherwise to dispose (or `onUnmount` lifecycle from React.Component
  // and you also don't need to set the position.y above
  return <abstractMesh fromInstance={box} position-y={positionY} disposeInstanceOnUnmount
     />
}

i want to do

app.js

import React, { Component } from 'react';
import { Scene, Engine,  Box, Sphere, SceneEventArgs, useScene} from 'react-babylonjs';
import { Vector3, MeshBuilder, FreeCamera, HemisphericLight ,Color3} from '@babylonjs/core';
import MySphere from './MySphere';

        function onSceneReady(e: SceneEventArgs) {
        		const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
                   <MySphere />
        	 }

        export default class Viewer extends Component {

               render() {
                return (
                    <Engine antialias adaptToDeviceRatio width={window.innerWidth} height={window.innerHeight}>
                        <Scene onSceneMount={onSceneReady}>
        				
                            <arcRotateCamera name='camera1' alpha={Math.PI / 2} beta={Math.PI / 4} radius={7.0} target={new Vector3(0, 0, 0)} />
                            <hemisphericLight name='light1' intensity={0.7} direction={Vector3.Up()} />
        						
                        </Scene>
                    </Engine>
                )
        	 }
        }

MySphere.js

import React, { Component } from 'react';
import { Scene, Engine, SphereCylinder,SceneEventArgs, useScene, onSceneMount} from 'react-babylonjs';
import { MeshBuilder} from '@babylonjs/core';


export default function MySphere(e: SceneEventArgs) {
		const sphere = MeshBuilder.CreateSphere("sphere", {size: 2}, e.scene);
		sphere.position.x = 1;
	 }

You cannot do that in React (not with DOM either) - it is just a function being called, so cannot return jsx and I am confused what you are trying to do. Your function is not possible:

function onSceneReady(e: SceneEventArgs) {
    const box = MeshBuilder.CreateBox("box", {size: 2}, e.scene);
    <MySphere />
}

To help you visualize - what you are writing in React DOM would look perhaps like this and is not possible:

function onClick(e) {
  const box = ...
  // this next line is not possible and is not valid syntax.  You cannot return jsx here either.
  <MySphere />
}
...
  <button onClick={onClick} />

A <div /> or Component in DOM can only be “returned” in a render method or functional component returned as jsx, but simply declaring it in a function as you have done is not possible. Inside the <Scene /> you can return anything from babylon - like <box /> or <freeCamera />, etc. My previous post showed you how I would declaratively build and also how it could be build imperatively. I would not use “onSceneReady” at all as I mentioned before - it’s just a way to provide access to Scene when it is ready, not so much for building out your scene graph.

hi @3dObject I’ve made you a code sandbox. Make changes there and let me know where you are stuck. It’s just making a couple of shapes and uses some hooks. It’s the same as the example from the homepage readme.
tender-rosalind-k11tu - CodeSandbox

thank you very much for help
here you have answered the entire answer that concerns me
I will get back to you if there is a problem

1 Like