pmndrs / react-three-fiber

🇨🇭 A React renderer for Three.js
https://docs.pmnd.rs/react-three-fiber
MIT License
27.4k stars 1.57k forks source link

Conceptual question about how to integrate other library code #551

Closed unphased closed 4 years ago

unphased commented 4 years ago

Hi, I'm trying to use other libraries with R3F. For example, I'm looking to use this code. I have linked to the load method on this loader. This is a library which is aware of and uses Three.js only.

I am learning about Suspense and am starting to understand how to add a small bit of code to the data fetching flow in order to allow the loading process to perform the required behaviors (throwing promises when fetches are not complete). It seems that the code related to loading must be updated to support this, as well as to introduce promises to it, and that is straightforward to me, no problem, I will fork and work on it.

The other question which I am having trouble navigating is this: On this loader, what I am being provided with is a derived class of Object3D (a base threejs class which R3F has component binding for). I just receive this raw three object out of this urdfLoader's load().

So I'm trying to just get a handle on what my options are. I did a very cursory look-see into R3F internals:

image

I can tell based on the fact that the file is named generated.d.ts that code generation is happening here to obtain the types for presenting all of three's types as react components.

  1. What would a mechanism look like for extending R3F to more types, types that i may be unable to easily port to R3F style? Does one already exist? Based on what I saw in generated.d.ts it does not look to me like I'd be able to manually come up with the correct types on my own, and would need to bring that code generation to bear on the new types I'm interested in using.
  2. To what extent would this mechanism be necessary? I can certainly work around this for now, it would just significantly reduce the beauty of the code. I'm still new to all this, but on first glance I can definitely use an imperative escape hatch with a ref to a leaf R3F component and hang the three object provided to me by the library onto it. In that way I could manually implement all of initialization, update, and cleanup imperatively, so I should be able to meet my requirements.

I think that doing that and not being able to further reactify the Object3D provided by the library is a wall that I hope it is possible to get past, even if it would be ugly for now in code. Could I get some pointers? I think if I'm gonna be adjusting the loading behavior to make it Suspense-capable then I may as well try to get it to provide bona fide R3F components.

drcmda commented 4 years ago

You can either use that loader as is in useEffect, then use state/set the result, or use useLoader which is more convenient. As for the result, you can project existing nodes into your declaratively graph via <primitive object={node} />. It behaves like a normal object from then on, you can put refs on it, position/rotate/scale it etc

So, essentially

const result = useLoader(URDLoader, url)
return <primitive object={result.scene} dispose={null} />

Given that URDLoader yields a scene ... The dispose switches off auto disposal, since loader results are cached.

You could also extract the geometry and materials from the loader output and still build the scene declaratively. That's what I would prefer. Search for "gltfjsx" it's a very powerful technique. This of course you can do with any loader manually, without tools.

unphased commented 4 years ago

Ah I see. I had come across the primitive trick before reading other issues. I should have been able to make that connection. Thanks. Oh do you mean “dispose” instead of “dispatch”? Extracting the data is quite a neat idea, I will try that! We have already started thinking about standardizing on GLTF, so this definitely seems like a good direction to go in.

I was thinking at first that useLoader would be the way to go but I first tried it out with a CubeTextureLoader and unfortunately ended up with weird behavior, so I have tempered my expectations for it. Though since that is the proper way to automatically make a loader suspense-capable, I guess I should revisit it sometime.

Thanks!

drcmda commented 4 years ago

yep, i meant dispose. useloader also works with cubetextureloader, but CTL expects an array: https://codesandbox.io/s/r3f-ibl-envmap-simple-p5zf9

const [cubeMap] = useLoader(HDRCubeTextureLoader, [['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', ...]])