MeshBuilder.ExtrudePolygon in React

Hi community,

I’m trying to build an updatable Polygon by using MeshBuilder.ExtrudePolygon method. Before, I writing as an non-declarative way by using Ref (React), then everytime I updated the parameters, it will take the current ref and dispose the old one, and creating new one base on new parameters.

Therefor, I have changed it to declarative way (because as the mesh grow, the ref grow), my idea is when I updated the shape props, the current polygon will update. But the Polygon don’t work as I expected (only update position and rotation), so I want to ask is there anyway to update the shape without dispose or using the ref.

Below is my current code:

If you need more information, I will be ready.
Thank you!

Sounds like a question for our friend @brianzinn :slight_smile:

2 Likes

Hi @hxxngvx - Welcome to the Babylon community.

That is a great question and describes the expected behaviour and I would like to go into the details of why that unexpected behaviour is expected by react-babylonjs. Sorry this is a bit long winded, but writing extra information here to help anybody else coming across this.

The way that the declarative version works is that it will call the MeshBuilder creation/factory method. When the <extrudePolygon /> is first encountered by React it will call that creation method, much like how a <div /> would be called by the DOM renderer with createElement.

The constructor will be called and then only the properties of the Mesh are updateable.

As an imperative example:

const box = MeshBuilder.CreateBox('box1', {depth: 2, ...}, scene);
// box is now a mesh. changing these properties will translate/scale/rotate mesh.  these are the same properties you have found are reflected in the mesh when they change
box.position = ...
box.rotation = ...
box.scaling = ...
// this is not allowed - "depth" was just used to procedurally generate the mesh
box.depth = ..

What you can do with a box is get a ref and update the position vertices data and I have done that before and did consider having this library update the vertices accordingly (and didn’t), but that will be much more complex for ExtrudePolygon and other shapes and also I wanted the react-babylonjs library to remain simple (KISS) and there is luckily an easy way to fix that, but it does dispose and create a new mesh when those constructor or factory method parameter(s) change and that is by using the “key” prop. The “key” prop lets the React reconciler know when an object is the same instance or not. You have probably seen the warnings when you are missing that property in a list.

In the case of a box, it would be as easy as:

  <box key={`depth-${post.thicknessValue}`} depth={post.thicknessValue} ... />

Now when the post.thicknessValue changes the old mesh will be disposed and a new one will replace it with the same position/rotation, but new depth/shape/etc.

In the case of ExtrudePolygon you would need to have a postSettings.polygon as a string representation and you could use something like joining the array to a string what I have found is that if you can then it would perform best to version your settings, so you would increment the “version” when constructor props change. It would be the same imperatively and that is the nature of creation/builder/factory methods. Have a look at this code line where that technique is used with <ExtrudeShape .../>:
babylonjs-directed-graph/App.tsx at master · brianzinn/babylonjs-directed-graph (github.com)

live demo here - when you add buildings the lines will be redrawn and their shape changes:
BabylonJS Directed Graph (brianzinn.github.io)

I update position vertices directly as a last resort and would try to use scaling (x/y/z are able to change “box” depth/height/width) if that was able to meet your needs. Let me know if that helps or if you have more questions.

1 Like

Thank you for mention @brianzinn :slight_smile:

Thank you @brianzinn for the suggestion! I will have a look on later today. If something approach, I will discuss more. Otherwise, I will mark for a solution. :slight_smile:

Hi @brianzinn , I was try to use the key attr in , but seam like when I update box’s attr, neither the width/height nor rotation/position change.

But if I create another functional component and inside that have another function to return the Box component like this bellow:


It will directly change as expected. (ExtrudePolygon work well with this way).

1 Like

hi @hxxngvx .

I’ve never seen forwardRef used like that. Is that the same as this except you forward props from the closure to an inline functional component?

const BoxBuilder = React.forwardRef((props, ref) => (
   <box ref={ref} enableEdgesRendering { ...props }>
      <standardMaterial ... />
  </box>
);

What I think is happening is that you are not re-using the same underlying babylonjs object and it’s being destroyed on every render because your component is declared inline.

I would recommend to use the host element case (camelCase instead of PascalCase). That indicates that it’s a host element (like div/span, which are handled directly by the renderer) and not a React component. I do support both though.

For the one that’s not working - I am not surprised that constructor arguments are not changing, because your “key” is probably not changing accordingly.

{seatProperties &&
    seatProperties.map((part) => (
      <box key={part.name} {...part} />
    ))
}

I would, however, expect “position” and “rotation” to update accordingly. I cannot see what properties are in your seatProperties object if they have position/rotation properties. Would need a more complete code sample to verify.

They key would need to contain more than a “name” that doesn’t change in order to cause the Mesh to be rebuilt from the changed constructor parameters. ie:

key={`${part.name}-${part.height}-${part.width}`}

Hi @brianzinn, thanks you for your response and feedback.

For “ref”:
Because it wasn’t re-render the box whenever the props change so I wrote like that to make that component to be always render.

For the “key”:
I understand the way of the key works, before I’m using key as mesh’s name which is static. I have changed to the one you mention and it’s works well as I expected.

Thank you! :slight_smile: