Open bigmistqke opened 1 year ago
I refactored the type of Instance
as described in this commit
Now I think our codebase is pretty much aligned with the current v9-branch of r3f.
I also added r3f as a sub-module into the repo, so that it's easy to compare them.
I personally think syncing their repo automatically with ours will be difficult to do, as there are little changes throughout the codebase. Instead I tried to mimmic their codebase as much as possible with ours so we can diff manually (I have been using the vs-extension Diff Folders
to do that, it's quite handy).
[for documentation purposes]
The initial way how children were attached to parents, in this branch, was done with the use of context
, from child to parent: each parent would wrap its children with a <Context.Provider value={() => Instance}>{props.children}</Context.Provider/>
and each child would check this context with the use of the useParent
-hook and attach itself to this parent. This however broke the expectation that only returned jsx would attach itself.
const Noop = () => {
const mesh = new <T.Mesh/>
return </></>
}
would actually connect the mesh
to its parent.
To fix this I adjusted the code (see b6cbde4) to create the hierarchy from parent-to-child instead of from child-to-parent.
Instead of using context we read the props.children
, from the parent, and attach the children to the parent accordingly. This way only returned jsx becomes a part of the tree.
I am looking to use this repo to create a solidjs renderer for pixi js and this maintenance and solid-js refactoring is massively appreciated. Looking forward to seeing where this PR goes :)
[for documentation purposes]
drei
port at solid-drei which helped this port too:
Portal
is fixeduseLoader
has been improvedT
with namespace SolidThree
and SolidThree.IntrinsicElements
(for three-elements and extend()
) and SolidThree.IntrinsicComponents
(for custom solid-three
components like T.Suspense
and T.Primitive
)r3f
has nice loader patterns in which their useLoader
throws immediately on creation and triggers suspense-boundaries. This way it's always guaranteed that their resources are always loaded. It has the downside that no resources are parallelized (afaik). Solid's way of doing Suspense is fundamentally different, so our previous implementation caused some friction when using Suspense
.r3f
behavior of suspending creation of three-elements until suspended boundary is resolved, while retaining parallel resource loading some work has been done:<T.Suspense/>
: includes a context with a { resolved, addResource }
-valueonSuspense
hook: to read the suspense-contextcreateThreeResource()
: a wrapper around createResource
that context.addResource
)
useLoader
uses createThreeResource
internally, all of drei's loaders are based on this primitive.<T.Primitive/>
and createThreeComponent
both contain code that read suspenseContext
from useSuspense
and that can suspend three-element creation in case !suspenseContext.resolved
.<T/>
-component or a <T.Primitive/>
:
T.Suspense
-context: Suspense
is resolve suspenseContext.resolved
Suspense
: Suspense
is resolveSuspense
is resolvedundefined
for the resource (ignoring the prop), the prop is applied once it is resolved (potentially even before the Suspense
is resolved) undefined
for the resource (ignoring the prop)Suspense
-inconsistencies where a <T.Mesh/>
would already be attached to the scene while its <T.Material/>
-child would have a resource.T.Suspense
and createThreeElement
, it could very well be an unnecessary complication. The initial inspection-run to collect resources together with regular Suspense
, resolving the tree from the bottom-up until the Suspense-boundary, could visually be enough. It might even be more performant, since it spreads out the creation of three-elements and the applying of props.// this could have performance benefits if texture is defined
const material = new THREE.Material({map: texture})
// I am not sure this would, since it would need to compile the shader first anyway
// except if THREE would queueMicrotask the compilation of the shader 🤔
const material = new THREE.Material()
material.map = texture
createLazyMemo
in <T.Primitive/>
and all other <T/>
-components and the double-run means effects need to be run twice (duh). All values are memo'd with mapArray
so I suppose the extra effects won't be too expensive, but there will be some overhead. @connorgmeehan cool! I would be pretty interested if there is a way to generalize this renderer, so you could plug it in other libraries. If you wanna come and hang out with us at discord
I removed all the timeline-management code and the suspension of three-element creation as they didn't compose nicely with ordinary jsx-elements and messed up some expectations, p.ex on when refs are resolved:
<T.Mesh ref={mesh}>
<CustomComponent mesh={mesh}/>
</T.Mesh>
In the above example CustomComponent
expects to receive mesh
immediately, but with the initial inspection-run this resulted into undefined.
@connorgmeehan cool! I would be pretty interested if there is a way to generalize this renderer, so you could plug it in other libraries. If you wanna come and hang out with us at discord
Sorry for the late response. It's funny you say that. That's one of the first things that I did when adapting this code.
Here's an early version of the generalised proxy renderer. I haven't really bothered open sourcing it the up to date code because I'm working in a private monorepo with my app.
https://github.com/sanspointes/constructables/
Basically it just lets you create a new root with a context and lets you wrap any classes into a component with automatic prop typing (which is honestly pretty buggy and slow for the typescript compiler). It also prefers defining implied props explicitly (such as position-x: number
) rather than at runtime so you get proper typing.
Also here's the pixi renderer implementation that uses the generalised renderer.
I am running into performance issues, especially surrounding the way props are tracked when components are created. I would love to combine efforts. :)
Also can you re-share the discord link? I can't open it.
This branch continues the
context-and-proxies
-branch from @vorth in which he took the proxy-implementation from @nksarafsolid-three-chess
branch ofreact-three-fiber
.This branch' intention is to remove all the dependencies and artefacts from
react
, remove the dependency onzustand
as an internal state manager in favour ofsolid/store
and streamline the code, while keeping as close as possible to r3f's v9 codebase.what has been done
.current
useThree
works different then r3f's, while they have selectors to subscribe to store-updates, with ouruseThree
is just a wrapper arounduseContext(ThreeContext)
, as we don't have to worry about unnecessary re-renders.core
now uses solid-store specific code to update the store, so the distinction betweencore
andsolid
directory is further made unclear.const state = store.getState()
and the like.store.subscribe()
has been replaced bycreateEffect(on(() => store, () => {…}))
which I am not 100% sure is the same.store. subscribe
might be deep-tracking bc off the whole immutable-thing.applyProps
to prevent unnecessary creations/cleanups of effects withmapArray(() => Object.keys(props), ...)
and checking if prop is possibly reactive (=== a getter or function
)solid/index.ts
is moved tosolid/renderer.ts
.core/renderer.ts
is removed. It contained some types it itself had inherit from the removed reconciler.ts, they have been moved tothree-types.ts
.index.ts
, ~solid/index.ts
~ (directories are merged, see below) andcore/index.ts
now only export filesapplyProps
three.js
).prettierrc
had the line"plugins": ["prettier-plugin-tailwindcss"]
which caused my prettier to not function. I replaced our.prettierrc
with the one of r3f to prevent unnecessary differences between codebases.core
andsolid
-repo back intocore
, more closely mimicking r3f's project-structurecore/components
tocore/proxy
(bc I thought it was a better parallel withreconciler
and i preferred how it looked lol)reflections
~Currently our codebase is somewhere between r3f's main branch and v9. For example,
Instance
is typed like the main branch, but we haveStages
like v9. Probably these changes happened after nikhil made his implementation (found pr when they made the change). Idk if we should pull everything closer to v9 or not. I think yes, but my previous attempts have all failed.~Instance has been ported to current v9 implementation. Our codebase is now up-to-date.
~As I said above; since we now have solid-store-related code throughout the codebase idk if it still makes sense to make the distinction between the two. I think mb it's best to just bring it back into 1 repo and try to mimic the structure as closely to r3f as possible, so we can at least manually compare the two when needed.~
I went ahead and merged those directories
I think it would probably be good to try out some more complex examples, to test out this version. Maybe port the examples of r3f.