Skip to content

Commit d4d2c18

Browse files
authored
feat: add support for <Entity from={Constructor} args={ConstructorParameters} />
Entity now supports both Three.js constructors and instances: - <Entity from={THREE.Mesh} args={[geometry, material]} /> - <Entity from={new THREE.Mesh(geometry, material)} />
2 parents 8ccc62b + 5016a67 commit d4d2c18

20 files changed

Lines changed: 307 additions & 197 deletions

playground/App.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { A, Route, Router } from "@solidjs/router"
22
import type { ParentProps } from "solid-js"
33
import * as THREE from "three"
44
import { Canvas, createT } from "../src/index.ts"
5-
import { SimpleSolar } from "./examples/SimpleSolar.tsx"
5+
import { SimplePortal } from "./examples/SimplePortal.tsx"
6+
import { SimpleSolarExample } from "./examples/SimpleSolar.tsx"
67
import "./index.css"
78

89
const T = createT(THREE)
@@ -43,6 +44,17 @@ function Layout(props: ParentProps) {
4344
>
4445
Simple Solar
4546
</A>
47+
<A
48+
href="/portal"
49+
style={{
50+
color: "white",
51+
"text-decoration": "none",
52+
padding: "5px 10px",
53+
display: "block",
54+
}}
55+
>
56+
Portal
57+
</A>
4658
</nav>
4759
{props.children}
4860
</>
@@ -52,7 +64,8 @@ function Layout(props: ParentProps) {
5264
export function App() {
5365
return (
5466
<Router root={Layout}>
55-
<Route path="/simple-solar" component={SimpleSolar} />
67+
<Route path="/simple-solar" component={SimpleSolarExample} />
68+
<Route path="/portal" component={SimplePortal} />
5669
<Route
5770
path="/"
5871
component={() => (
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as THREE from "three"
2+
import { Canvas, createT, Entity, Portal } from "../../src/index.ts"
3+
4+
const T = createT(THREE)
5+
6+
export function SimplePortal() {
7+
const group = new THREE.Group()
8+
return (
9+
<Canvas
10+
style={{ width: "100vw", height: "100vh" }}
11+
camera={{ position: new THREE.Vector3(0, 0, 30) }}
12+
onClick={event => console.debug("canvas clicked", event)}
13+
onClickMissed={event => console.debug("canvas click missed", event)}
14+
onPointerLeave={event => console.debug("canvas pointer leave", event)}
15+
onPointerEnter={event => console.debug("canvas pointer enter", event)}
16+
>
17+
<Entity from={group} position={[0, 5, 0]} />
18+
<Portal element={group}>
19+
<T.Mesh>
20+
<T.SphereGeometry args={[2, 32, 32]} />
21+
<T.MeshBasicMaterial color="red" />
22+
</T.Mesh>
23+
</Portal>
24+
</Canvas>
25+
)
26+
}

playground/examples/SimpleSolar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function CelestialBody(
116116
)
117117
}
118118

119-
export function SimpleSolar() {
119+
export function SimpleSolarExample() {
120120
return (
121121
<Canvas
122122
style={{ width: "100vw", height: "100vh" }}

src/augment.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/components.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { type JSX, type ParentProps, createMemo, createRenderEffect, mergeProps } from "solid-js"
22
import { Object3D } from "three"
3-
import { augment } from "./augment.ts"
43
import { threeContext, useThree } from "./hooks.ts"
54
import { manageSceneGraph, useProps } from "./props.ts"
6-
import type { Instance, Props, ThreeInstance } from "./types.ts"
5+
import type { Instance, Props } from "./types.ts"
76
import { type InstanceFromConstructor } from "./types.ts"
8-
import { isInstance } from "./utils/is-instance.ts"
9-
import { withContext } from "./utils/with-context.ts"
7+
import { augment, autodispose, isConstructor, isInstance, withContext } from "./utils.ts"
8+
9+
/**********************************************************************************/
10+
/* */
11+
/* Portal */
12+
/* */
13+
/**********************************************************************************/
1014

1115
type PortalProps = ParentProps<{
1216
element?: InstanceFromConstructor<Object3D> | Instance<Object3D>
@@ -48,10 +52,14 @@ export const Portal = (props: PortalProps) => {
4852
return null
4953
}
5054

51-
type EntityProps<T> = Omit<Props<T>, "object" | "children" | "ref" | "args"> & {
55+
/**********************************************************************************/
56+
/* */
57+
/* Entity */
58+
/* */
59+
/**********************************************************************************/
60+
61+
type EntityProps<T extends object | (new (...args: any[]) => any)> = Omit<Props<T>, "from"> & {
5262
from: T
53-
children?: JSX.Element
54-
ref?: T | ((value: T) => void)
5563
}
5664
/**
5765
* Wraps a `ThreeElement` and allows it to be used as a JSX-component within a `solid-three` scene.
@@ -62,8 +70,13 @@ type EntityProps<T> = Omit<Props<T>, "object" | "children" | "ref" | "args"> & {
6270
* optional children, and a ref that provides access to the object instance.
6371
* @returns The Three.js object wrapped as a JSX element, allowing it to be used within Solid's component system.
6472
*/
65-
export function Entity<T extends ThreeInstance>(props: EntityProps<T>) {
66-
const memo = createMemo(() => augment(props.from, { props }) as Instance<T>)
73+
export function Entity<T extends object | (new (...args: any[]) => object)>(props: EntityProps<T>) {
74+
const memo = createMemo(() => {
75+
return augment(
76+
isConstructor(props.from) ? autodispose(new props.from(...(props.args ?? []))) : props.from,
77+
{ props },
78+
) as Instance<T>
79+
})
6780
useProps(memo, props)
6881
return memo as unknown as JSX.Element
6982
}

src/create-events.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Object3D, type Intersection } from "three"
22
import type { CanvasProps } from "./canvas.tsx"
33
import { $S3C } from "./constants.ts"
44
import type { Context, EventName, Instance, ThreeEvent } from "./types.ts"
5-
import { isInstance } from "./utils/is-instance.ts"
5+
import { isInstance } from "./utils.ts"
66

77
const eventNameMap = {
88
onClick: "click",

src/create-t.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createMemo, type JSX, mergeProps } from "solid-js"
2-
import { augment } from "./augment.ts"
32
import { useProps } from "./props.ts"
43
import type { Component } from "./types.ts"
4+
import { augment } from "./utils.ts"
55

66
/**********************************************************************************/
77
/* */

src/create-three.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,15 @@ import {
2323
Vector2,
2424
WebGLRenderer,
2525
} from "three"
26-
import { augment } from "./augment.ts"
2726
import type { CanvasProps } from "./canvas.tsx"
2827
import { createEvents } from "./create-events.ts"
2928
import { AugmentedStack } from "./data-structure/augmented-stack.ts"
3029
import { frameContext, threeContext } from "./hooks.ts"
3130
import { canvasPropsContext, eventContext } from "./internal-context.ts"
3231
import { manageSceneGraph, useProps } from "./props.ts"
3332
import type { CameraType, Context } from "./types.ts"
34-
import { defaultProps } from "./utils/default-props.ts"
35-
import { removeElementFromArray } from "./utils/remove-element-from-array.ts"
33+
import { augment, defaultProps, removeElementFromArray, withMultiContexts } from "./utils.ts"
3634
import { useMeasure } from "./utils/use-measure.ts"
37-
import { withMultiContexts } from "./utils/with-context.ts"
3835

3936
/**
4037
* Creates and manages a `solid-three` scene. It initializes necessary objects like
@@ -109,7 +106,7 @@ export function createThree(canvas: HTMLCanvasElement, props: CanvasProps) {
109106
}
110107
pendingRenderRequest = undefined
111108
context.gl.render(context.scene, context.camera)
112-
frameListeners.forEach(listener => listener(context, timestamp, frame))
109+
frameListeners.forEach(listener => listener(context, context.clock.getDelta(), frame))
113110
}
114111
function requestRender() {
115112
if (pendingRenderRequest) return
@@ -132,9 +129,12 @@ export function createThree(canvas: HTMLCanvasElement, props: CanvasProps) {
132129
const measure = useMeasure()
133130
measure.setElement(canvas)
134131

132+
const clock = new Clock()
133+
clock.start()
134+
135135
const context: Context = {
136136
canvas,
137-
clock: new Clock(),
137+
clock,
138138
get bounds() {
139139
return measure.bounds()
140140
},

src/data-structure/augmented-stack.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Accessor } from "solid-js"
22
import type { Instance } from "src/types.ts"
3-
import { augment } from "../augment.ts"
3+
import { augment } from "../utils.ts"
44
import { Stack } from "./stack.ts"
55

66
/** A generic stack data structure. It augments each value before pushing it onto the stack. */

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ export { createT, createTComponent } from "./create-t.tsx"
55
export { useFrame, useLoader, useThree } from "./hooks.ts"
66
export { applyProps, useProps } from "./props.ts"
77
export * as S3 from "./types.ts"
8-
export { buildGraph } from "./utils/build-graph.ts"
8+
export { autodispose, buildGraph } from "./utils.ts"

0 commit comments

Comments
 (0)