-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define declarativelly an imported mesh #81
Comments
You want to declaratively have your meshes show up from your model and attach materials. <model ...>
<standardMaterial key={'mesh0mat'} assignTo={'meshes[0].material'} diffuseColor={props.part1Color} />
<standardMaterial key={'mesh1mat'} assignTo={'meshes[1].material'} diffuseColor={props.part2Color} />
</model> What you are trying to do does not work currently the |
Ey @brianzinn ! Sorry for the late response. I would like to try the solution you are suggesting. I'm a newbie in WebGL and Babylon to be honest, but I've been reading about the AssetContainer, and it sounds like it could be a solution. I will be glad to help in any way you think I might. I suppose I could do some sort of test with storybook by forking the library so we can check what you are proposing, but not sure about the tradeoffs. What would you choose? To give you some context, I'm in some sort of spike to develope an application that allows you to visualize and modify a custom 3D model. I've been playing with three.js and react-three-fiber also, but I think babylon would solve better some features that I want to use and babylone can give me out of the box (glow effects for example). So I would like to turn in an off effects and change colors in a mesh/model. One of the thinks I really liked of react-spring is that they have this sort of script that converts a gltf to a authomatic generated group of meshes in react-three-fiber https://github.com/react-spring/gltfjsx It was a really good dev experience, since I could just change the materials in a declarative way very easy, as I described in my first message. |
Ey @brianzinn, I figured out how to achive what I was looking for. I did an implementation of a loder hook using the AssetContainer, but maybe is not te best implementation to include it as a new asset in the library. Would you want to share it with you an discuss on how can we do it properly? Also, really interested in the Cheers! |
I think a fromObject would solve a bunch of current issues - I know how to add that and it’s not a big change. If you could share your code or do a PR then we can get that into the library. I’ll add the fromObject in the next few days. |
hi @mariosanchez - I got the part in the reconciler done, so you can assign meshes and declaratively attach materials/textures. const MyMesh = (props) => {
const [mesh, setMesh] = useState(null);
const scene = useBabylonScene();
useMemo(() => {
console.log('creating a box with scene', scene);
setMesh(MeshBuilder.CreateBox('test', { size: 1}, scene));
}, []);
return (
<>
{mesh &&
<mesh fromInstance={mesh} rotation={props.rotation}>
<standardMaterial name='boxmat' diffuseColor={Color3.Blue()} specularColor={Color3.Black()} />
</mesh>
}
</>
)
} That is on a branch called |
hi @brianzinn, I've trying the fromInstance prop and it works lovely so far. To put an example of how the code changed: Before import React, { useEffect, useRef } from 'react';
function Part({ partMesh, material }) {
const partName = useRef(null);
partMesh.setEnabled(true);
useEffect(() => {
partMesh.setParent(partName.current.hostInstance);
}, [partName]);
partMesh.material = material;
return <transformNode name={`part_${partMesh.name}`} ref={partName} />;
}
export default Part; After import React from 'react';
function Part({ partMesh, material }) {
partMesh.setEnabled(true);
partMesh.material = material;
return <mesh fromInstance={partMesh} />;
}
export default Part; |
About the solution I got for not immediatelly display the loaded meshes in the scene I got some inspiration in that post in the babylon forum https://forum.babylonjs.com/t/load-mesh-without-adding-to-scene/606/12 and the useLoader of the library. I think it's far from being a generic solution, and maybe other aproach could be better, but I will exemplify it. I have this useAssetManager hook (didn't think much about the naming tbh), which hides the loaded meshes: import { useEffect, useState } from 'react';
import '@babylonjs/loaders';
import { useBabylonScene } from 'react-babylonjs';
import { AssetsManager } from '@babylonjs/core';
export function useAssetManager(rootUrl, meshNames, sceneFilename) {
const scene = useBabylonScene();
const [loaded, setLoaded] = useState(false);
const [result, setResult] = useState([]);
const assetsManager = new AssetsManager(scene);
useEffect(() => {
loadMesh(assetsManager, rootUrl, meshNames, sceneFilename)
.then(({ loadedMeshes }) => {
setResult(
loadedMeshes.map((mesh) => {
mesh.setEnabled(false);
return mesh;
})
);
setLoaded(true);
})
.catch((error) => console.error(error));
assetsManager.load();
}, [rootUrl, sceneFilename]);
return [loaded, result];
}
function loadMesh(assetsManager, rootUrl, meshNames, sceneFilename) {
return new Promise((resolve, reject) => {
const meshTask = assetsManager.addMeshTask(
`load ${rootUrl}`,
meshNames,
rootUrl,
sceneFilename
);
meshTask.onSuccess = function onMeshTaskSuccess(task) {
resolve(task);
};
meshTask.onError = function onMesshTaskError(_, message, exception) {
reject(exception ?? message);
};
});
} Then I use it like this: function Component() {
const url = '/gltf/aMode.glb';
const [loaded, meshes] = useAssetManager(url, [ /* a mesh names list */ ]);
return useMemo(() => {
if (loaded) {
const parts = meshes.reduce(
(partialParts, mesh) => ({ ...partialParts, [mesh.id]: mesh }),
{}
);
return (
<transformNode name="parent">
<Part
partMesh={parts['aPartName']}
material={aMaterial}
/>
<Part
partMesh={parts['aPartName']}
material={aMaterial}
/>
// ...
</transformNode>
);
}
return null;
}, [loaded, state.parts]);
} Using it like this you are not getting all the features of a AssetManager, I'm just using it this way to hide the mesh when it's loeaded. Maybe ther is some sort of modification we could do in the existing loader to work the same way with an option parameter to hide or display the meshes? The problem I found with the original loader is that it duplicate the meshes it returns, the reference is no the same soy you end with two meshes in the scene that are independent, and that was not what I wanted. Also, the AssetManager displays a loading screen by default that you probably just want to disable and put yours. I feel like probably we want to define a proper interface for this feature before get a step further. What are yout thoughs? |
I added a hook |
Hi @brianzinn Thanks for your work! It's for sure a good start. And so cool you could add suspense support to it. One doubt I have, is that you commented that useMemo is failing. Is it acctually throwing an error or it's just it has no effect because of you are throwing a promise? Are you planning to ship this to npm together with the |
I did ship an NPM with You are correct that I put it on a branch - going to try to get some time to clean that all up. Does that have all the functionality that you would need? You should be able to disable meshes as you are above. I added support for MeshTasks in this branch and a storybook example for progress: |
Yes it does. I will check the branch anyway, but keep going. We could close the issue if you are okay with this :) Thanks again! |
Thanks for confirming it works! I'll leave open and will let you know when next version is ready - probably v3.0 as it will have breaking changes with switch over to Suspense. I like how you made a map of mesh ids (parts), I am thinking to add something like that to final API for task lookup by name. |
hi @mariosanchez - going to close as 3.0 is out, but have a look at how I created the The hook returns a meshMap you can use to set props as you originally described. You need on that hook. Feel free to re-open if you have any more comments/questions. Thanks for your help getting this across the finish line (and patience)!! |
Hi!
I'm trying to use useLoader() to load a .glb file.
My goal is to be hable to load a mesh, and declare the diferent parts of the imported mesh as different meshes in order to redeclare them in a declarative way and then be hable to change it's materials and props with a state management system or component state:
Something like:
The problem I'm facing is that I think useLoader appends the imported mesh to the scene immediatelly because I have not been hable to load a single part of the imported mesh or maybe I'm doing something wrong.
The text was updated successfully, but these errors were encountered: