@lukekaalim/act-three 4.0.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ // @flow strict
2
+ /*:: import type { Component, Ref } from '@lukekaalim/act'; */
3
+ /*:: import type { Euler, Group, Object3D } from "three"; */
4
+ /*:: import type { GroupProps, RefProp } from '../components'; */
5
+
6
+ import { h, useEffect, useRef, useState } from '@lukekaalim/act';
7
+ import { Matrix4, Vector3 } from "three";
8
+ import { group } from '../components.js';
9
+
10
+ /*::
11
+ export type LookAtGroupProps = {
12
+ ...$Diff<GroupProps, { rotation?: Euler, ref?: RefProp<Group> }>,
13
+ target: Vector3,
14
+ deps?: mixed[],
15
+ };
16
+ */
17
+
18
+ export const LookAtGroup/*: Component<LookAtGroupProps>*/ = ({
19
+ target,
20
+ children,
21
+ position = new Vector3(0, 0, 0),
22
+ deps = [],
23
+ ...groupProps
24
+ }) => {
25
+ const groupRef = useRef(null);
26
+
27
+ useEffect(() => {
28
+ const { current: groupNode } = groupRef;
29
+ if (!groupNode)
30
+ return;
31
+
32
+ groupNode.setRotationFromMatrix(new Matrix4().lookAt(position, target, new Vector3(0, 1, 0)));
33
+ groupNode.updateMatrix();
34
+ }, [target.x, target.y, target.z, position.x, position.y, position.z, ...deps]);
35
+
36
+ return h(group, { ...groupProps, position, ref: groupRef }, children);
37
+ }
@@ -0,0 +1 @@
1
+ // @flow strict
package/components.js CHANGED
@@ -21,19 +21,10 @@
21
21
  /*:: import * as Three from 'three'; */
22
22
 
23
23
  /*::
24
- export type RootProps = {
25
- canvas?: ?{ [string]: mixed },
26
- renderer?: ?{
27
- size?: ?{ width: number, height: number, updateStyle: boolean },
28
- clearColor?: ?{ color: Color, alpha: number },
29
- clearAlpha?: number,
30
- },
31
- onRender?: ?(timestamp: number, renderer: WebGLRenderer, scene: Object3D) => mixed,
32
- camera?: ?Camera,
33
- };
24
+ export type RefProp<T> = ((reference: T) => mixed) | { current: T | any }
34
25
 
35
26
  export type Object3DProps<T> = {
36
- ref?: ((reference: T) => mixed) | { current: T | any },
27
+ ref?: RefProp<?T>,
37
28
 
38
29
  name?: string,
39
30
  position?: Vector3,
@@ -73,6 +64,18 @@ export type PointLightsProps = {
73
64
  intensity?: number,
74
65
  power?: number,
75
66
  };
67
+ export type DirectionalLightProps = {
68
+ ...Object3DProps<Three.DirectionalLight>,
69
+ distance?: number,
70
+ decay?: number,
71
+ intensity?: number,
72
+ power?: number,
73
+ };
74
+ export type AmbientLightProps = {
75
+ ...Object3DProps<Three.AmbientLight>,
76
+ intensity?: number,
77
+ power?: number,
78
+ };
76
79
  export type PointsProps = {
77
80
  ...Object3DProps<Three.Points>,
78
81
  geometry?: BufferGeometry,
@@ -90,17 +93,31 @@ export type PerspectiveCameraProps = {
90
93
  fov?: number,
91
94
  zoom?: number,
92
95
  }
96
+ export type OrthographicCameraProps = {
97
+ ...Object3DProps<Three.OrthographicCamera>,
98
+ aspect?: number,
99
+ near?: number,
100
+ far?: number,
101
+ zoom?: number,
102
+ }
93
103
  */
94
104
 
95
- export const three = ('three'/*: Component<RootProps>*/);
96
-
97
105
  export const scene = ('scene'/*: Component<SceneProps>*/);
98
106
  export const group = ('group'/*: Component<GroupProps>*/);
99
107
 
100
108
  export const perspectiveCamera = ('perspectiveCamera'/*: Component<PerspectiveCameraProps>*/);
109
+ export const orthographicCamera = ('orthographicCamera'/*: Component<OrthographicCameraProps>*/);
101
110
 
102
111
  export const mesh = ('mesh'/*: Component<MeshProps>*/);
103
112
  export const sprite = ('sprite'/*: Component<SpriteProps>*/);
104
113
  export const points = ('points'/*: Component<PointsProps>*/);
105
114
  export const instancedMesh = ('instancedMesh'/*: Component<InstancedMeshProps>*/);
106
- export const pointLight = ('pointLight'/*: Component<PointLightsProps>*/);
115
+
116
+ export const pointLight = ('pointLight'/*: Component<PointLightsProps>*/);
117
+ export const ambientLight = ('ambientLight'/*: Component<AmbientLightProps>*/);
118
+ export const hemisphereLight = ('hemisphereLight'/*: Component<PointLightsProps>*/);
119
+ export const directionalLight = ('directionalLight'/*: Component<DirectionalLightProps>*/);
120
+
121
+
122
+ export * from './components/groups.js';
123
+ export * from './components/lights.js';
package/entry.js CHANGED
@@ -3,4 +3,5 @@
3
3
  export * from './reconciler.js';
4
4
  export * from './renderer.js';
5
5
  export * from './objects.js';
6
- export * from './components.js';
6
+ export * from './components.js';
7
+ export * from './hooks.js';
@@ -0,0 +1,28 @@
1
+ // @flow strict
2
+ /*:: import type { Component, Ref } from '@lukekaalim/act'; */
3
+ /*:: import type { Euler, Group, Object3D } from "three"; */
4
+ /*:: import type { GroupProps, RefProp } from '../components'; */
5
+
6
+ import { h, useEffect, useRef, useState } from '@lukekaalim/act';
7
+ import { Matrix4, Vector3 } from "three";
8
+ import { group } from '../components.js';
9
+
10
+ export const defaultUp/*: Vector3*/ = new Vector3(0, 1, 0);
11
+
12
+ export const useLookAt = /*:: <T: Object3D>*/(
13
+ objectRef/*: Ref<?T>*/,
14
+ target/*: Vector3*/,
15
+ deps/*: mixed[]*/ = [],
16
+ up/*: Vector3*/ = defaultUp,
17
+ ) => {
18
+
19
+ useEffect(() => {
20
+ const { current: object } = objectRef;
21
+ if (!object)
22
+ return;
23
+
24
+ object.setRotationFromMatrix(new Matrix4().lookAt(object.position, target, up));
25
+ object.updateMatrix();
26
+
27
+ }, [target.x, target.y, target.z, ...deps]);
28
+ }
package/hooks.js ADDED
@@ -0,0 +1,177 @@
1
+ // @flow strict
2
+ /*:: import type { Ref } from '@lukekaalim/act'; */
3
+ /*:: import type { Camera, Scene, Texture } from "three"; */
4
+ import { TextureLoader, Vector2, WebGLRenderer } from "three";
5
+
6
+ import { useEffect, useState } from "@lukekaalim/act";
7
+ import { useRef } from "@lukekaalim/act";
8
+
9
+
10
+ /*::
11
+ export type WebGLOptions = {
12
+ antialias?: boolean,
13
+ alpha?: boolean,
14
+
15
+ shadowMap?: {
16
+ enabled?: boolean,
17
+ autoUpdate?: boolean,
18
+ type?: number,
19
+ }
20
+ };
21
+ */
22
+
23
+ export const useWebGLRenderer = (
24
+ canvasRef/*: Ref<?HTMLCanvasElement>*/,
25
+ options/*: WebGLOptions*/ = {},
26
+ deps/*: mixed[]*/ = []
27
+ )/*: ?WebGLRenderer*/ => {
28
+ const [renderer, setRenderer] = useState/*:: <?WebGLRenderer>*/(null);
29
+ useEffect(() => {
30
+ const { current: canvas } = canvasRef;
31
+ if (!canvas)
32
+ return;
33
+
34
+ const renderer = new WebGLRenderer({ ...options, canvas });
35
+ renderer.shadowMap.enabled = options.shadowMap?.enabled || false;
36
+ setRenderer(renderer);
37
+
38
+ return () => {
39
+ setRenderer(null);
40
+ renderer.dispose();
41
+ }
42
+ }, deps);
43
+
44
+ return renderer;
45
+ };
46
+
47
+ // Resize the canvas's resolution whenever it changes size
48
+ // useful for when the canvas is some percentage of the screen
49
+ // like: width: 100%
50
+ export const useResizingRenderer = (
51
+ canvasRef/*: Ref<?HTMLCanvasElement>*/,
52
+ renderer/*: ?WebGLRenderer*/
53
+ )/*: ?Vector2*/ => {
54
+ const [size, setSize] = useState(null)
55
+ useEffect(() => {
56
+ const { current: canvas } = canvasRef;
57
+ if (!canvas || !renderer)
58
+ return null;
59
+
60
+ const observer = new ResizeObserver((entries) => {
61
+ for (const entry of entries.filter(e => e.target === canvas)) {
62
+ const width = entry.contentBoxSize[0].inlineSize;
63
+ const height = entry.contentBoxSize[0].blockSize;
64
+ const size = new Vector2(width, height);
65
+ setSize(size);
66
+ renderer.setSize(width, height, false);
67
+ }
68
+ });
69
+ observer.observe(canvas, { box: 'content-box' });
70
+
71
+ return () => {
72
+ observer.unobserve(canvas);
73
+ setSize(null);
74
+ }
75
+ }, [renderer]);
76
+
77
+ return size;
78
+ }
79
+
80
+ export const useAnimationFrame = (
81
+ onAnimate/*: ?(now: DOMHighResTimeStamp, delta: number) => mixed*/ = null,
82
+ deps/*: mixed[]*/ = []
83
+ ) => {
84
+ useEffect(() => {
85
+ if (!onAnimate)
86
+ return;
87
+
88
+ let lastFrame = performance.now();
89
+ const animate = (now) => {
90
+ const delta = now - lastFrame;
91
+ lastFrame = now;
92
+
93
+ onAnimate(now, delta);
94
+ id = requestAnimationFrame(animate);
95
+ };
96
+ let id = requestAnimationFrame(animate);
97
+
98
+ return () => {
99
+ if (id)
100
+ cancelAnimationFrame(id);
101
+ }
102
+ }, deps);
103
+ }
104
+
105
+ export const useRenderLoop = (
106
+ renderer/*: ?WebGLRenderer*/,
107
+ camera/*: Ref<?Camera>*/,
108
+ scene/*: Ref<?Scene>*/,
109
+ onAnimate/*: (now: DOMHighResTimeStamp, delta: number) => mixed*/ = (_, __) => {},
110
+ deps/*: mixed[]*/ = []
111
+ ) => {
112
+ const renderFunction = renderer && ((now, delta) => {
113
+ onAnimate(now, delta);
114
+ if (camera.current && scene.current)
115
+ renderer.render(scene.current, camera.current);
116
+ });
117
+
118
+ useEffect(() => {
119
+ if (renderFunction)
120
+ renderFunction(performance.now(), 0);
121
+ }, [renderer, ...deps])
122
+
123
+ useAnimationFrame(renderFunction, [renderer, ...deps])
124
+ }
125
+
126
+ /*::
127
+ export interface Disposable {
128
+ dispose(): void;
129
+ }
130
+ */
131
+
132
+ export const useDisposable = /*:: <T: Disposable>*/(
133
+ create/*: () => T*/,
134
+ deps/*: mixed[]*/ = []
135
+ )/*: T*/ => {
136
+ const [resource, setResource] = useState/*:: <T>*/(() => create());
137
+ const disposedRef = useRef(false);
138
+
139
+ useEffect(() => {
140
+ const { current: disposed } = disposedRef
141
+
142
+ if (disposed) {
143
+ setResource(create());
144
+ disposedRef.current = false;
145
+ }
146
+
147
+ return () => {
148
+ resource.dispose();
149
+ disposedRef.current = true;
150
+ }
151
+ }, deps);
152
+ return resource;
153
+ };
154
+
155
+ export const useTexture = (url/*: string*/)/*: Texture*/ => {
156
+ const [texture, setTexture] = useState(null)
157
+ useEffect(() => {
158
+ const loader = new TextureLoader();
159
+
160
+ const texture = loader.load(url);
161
+ setTexture(texture);
162
+
163
+ console.log(loader);
164
+
165
+ return () => {
166
+ texture.dispose();
167
+ }
168
+ }, []);
169
+
170
+ if (!texture)
171
+ throw { type: 'loading' };
172
+
173
+ return texture;
174
+ };
175
+
176
+
177
+ export * from './hooks/matrix.js';
package/objects.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // @flow strict
2
2
  /*:: import type { Object3D } from "three"; */
3
3
  /*:: import type { PropDiff } from '@lukekaalim/act-reconciler'; */
4
- /*:: import type { ObjectDef } from './renderer.js'; */
5
4
  import {
6
5
  Group,
7
6
  Mesh,
@@ -10,24 +9,42 @@ import {
10
9
  Points,
11
10
  Scene,
12
11
  InstancedMesh,
12
+ OrthographicCamera,
13
+ DirectionalLight,
14
+ HemisphereLight,
15
+ AmbientLight,
13
16
  } from "three";
14
17
 
18
+ const threeObjectClasses/*: { [string]: Class<Object3D> }*/ = Object.fromEntries(Object.entries({
19
+ // grouping
20
+ Scene,
21
+ Group,
22
+
23
+ // lights
24
+ DirectionalLight,
25
+ HemisphereLight,
26
+ PointLight,
27
+ AmbientLight,
28
+
29
+ // camera
30
+ PerspectiveCamera,
31
+ OrthographicCamera,
32
+
33
+ // lines
34
+ Points,
35
+
36
+ // mesh
37
+ Mesh,
38
+ InstancedMesh,
39
+ }).map(([key, value]) => [key.toLowerCase(), (value/*: any*/)]));
40
+
15
41
  export const createObject = (type/*: string*/)/*: null | Object3D*/ => {
16
- switch (type) {
17
- case 'scene':
18
- return new Scene();
19
- case 'group':
20
- return new Group();
21
- case 'perspectiveCamera':
22
- return new PerspectiveCamera();
23
- case 'points':
24
- return new Points();
25
- case 'mesh':
26
- return new Mesh();
27
- case 'instancedMesh':
28
- return new InstancedMesh();
29
- default:
30
- console.warn(`Unknown object type "${type}"`)
31
- return null;
42
+ const objectClass = threeObjectClasses[type.toLowerCase()];
43
+
44
+ if (!objectClass) {
45
+ console.warn(`Unknown object type "${type}"`)
46
+ return null;
32
47
  }
48
+
49
+ return new objectClass();
33
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukekaalim/act-three",
3
- "version": "4.0.0",
3
+ "version": "5.2.1",
4
4
  "description": "Render threejs content using act",
5
5
  "main": "entry.js",
6
6
  "type": "module",
@@ -16,8 +16,8 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@lukekaalim/act": "^2.2.0",
19
- "@lukekaalim/act-renderer-core": "^2.0.0",
20
- "@lukekaalim/act-reconciler": "^2.0.4",
21
- "@lukekaalim/act-web": "^1.0.0"
19
+ "@lukekaalim/act-renderer-core": "^3.1.1",
20
+ "@lukekaalim/act-reconciler": "^3.0.1",
21
+ "@lukekaalim/act-web": "^1.2.0"
22
22
  }
23
23
  }
package/props.js CHANGED
@@ -1,84 +1,12 @@
1
1
  // @flow strict
2
2
  /*:: import type { Object3D, WebGLRenderer } from "three"; */
3
3
  /*:: import type { Props, PropDiff, CommitDiff } from '@lukekaalim/act-reconciler'; */
4
- /*:: import type { Frame, Root } from "./renderer.js"; */
5
4
 
6
5
  import { calculatePropsDiff } from "@lukekaalim/act-reconciler";
7
6
  import { setHTMLProp } from "@lukekaalim/act-web";
8
- import { Group } from "three";
7
+ import { DirectionalLight, Group, Light, OrthographicCamera } from "three";
9
8
  import { setRef } from "@lukekaalim/act-renderer-core";
10
9
 
11
- export const setCanvasProps = ({ prev, next }/*: PropDiff*/, canvas/*: HTMLCanvasElement*/) => {
12
- const validPrev = typeof prev === 'object' && prev ? prev : {};
13
- const validNext = typeof next === 'object' && next ? next : {};
14
- const diffs = calculatePropsDiff(validPrev, validNext)
15
- for (const [key, prop] of diffs)
16
- setHTMLProp(canvas, prop);
17
- };
18
-
19
- export const setWebGLRendererProp = ({ key, prev, next }/*: PropDiff*/,renderer/*: WebGLRenderer*/) => {
20
- switch (key) {
21
- case 'size':
22
- if (typeof next !== 'object' || !next)
23
- return;
24
- const { width, height, updateStyle } = next;
25
- // $FlowFixMe
26
- renderer.setSize(width, height, updateStyle);
27
- return;
28
- case 'clearAlpha':
29
- // $FlowFixMe
30
- renderer.setClearAlpha(next);
31
- case 'clearColor':
32
- if (typeof next !== 'object' || !next)
33
- return;
34
- const { color, alpha } = next;
35
- // $FlowFixMe
36
- renderer.setClearColor(color, alpha);
37
- }
38
- };
39
-
40
- export const setWebGLRendererProps = ({ prev, next }/*: PropDiff*/, renderer/*: WebGLRenderer*/) => {
41
- const validPrev = typeof prev === 'object' && prev ? prev : {};
42
- const validNext = typeof next === 'object' && next ? next : {};
43
- const diffs = calculatePropsDiff(validPrev, validNext)
44
-
45
- for (const [key, prop] of diffs)
46
- setWebGLRendererProp(prop, renderer);
47
- };
48
-
49
- const defaultRootProps/*: Props*/ = {
50
- camera: null,
51
- onRender: _ => {},
52
- };
53
-
54
- export const setRootProps = (diff/*: CommitDiff*/, { frame, renderer, canvas }/*: Root*/, children/*: Object3D[]*/) => {
55
- const validPrev = { ...defaultRootProps, ...diff.prev.element.props };
56
- const validNext = { ...defaultRootProps, ...diff.next.element.props };
57
- const diffs = calculatePropsDiff(validPrev, validNext);
58
-
59
- if (diffs.has('camera') || diffs.has('onRender')) {
60
- const { onRender, camera } = (validNext/*: any*/);
61
- if (frame.id)
62
- cancelAnimationFrame(frame.id);
63
- if (!camera)
64
- renderer.clear(true, true, true);
65
- if (onRender && camera && children[0]) {
66
- const updateFunction = (timestamp) => {
67
- onRender(timestamp, renderer, children[0]);
68
- renderer.render(children[0], camera);
69
- frame.id = requestAnimationFrame(updateFunction);
70
- };
71
- frame.id = requestAnimationFrame(updateFunction);
72
- }
73
- }
74
- const canvasProps = diffs.get('canvas');
75
- const rendererProps = diffs.get('renderer');
76
- if (canvasProps)
77
- setCanvasProps(canvasProps, canvas);
78
- if (rendererProps)
79
- setWebGLRendererProps(rendererProps, renderer);
80
- };
81
-
82
10
  export const setObjectProp = ({ key, prev, next }/*: PropDiff*/, object/*: Object3D*/)/*: void*/ => {
83
11
  switch (key) {
84
12
  case 'position':
@@ -112,12 +40,36 @@ export const setGroupProps = (diff/*: CommitDiff*/, group/*: Group*/) => {
112
40
  }
113
41
  };
114
42
 
43
+ export const setDirectionalLightProps = (diff/*: CommitDiff*/, light/*: DirectionalLight*/) => {
44
+ const diffs = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
45
+ for (const [key, diff] of diffs) {
46
+ switch (key) {
47
+ case 'shadow': {
48
+ const diffs = calculatePropsDiff((diff.prev/*: any*/) || {}, (diff.next/*: any*/) || {});
49
+ for (const [key, diff] of diffs)
50
+ (light/*: any*/).shadow[key] = diff.next;
51
+ break;
52
+ }
53
+ default:
54
+ setObjectProp(diff, light); break;
55
+ }
56
+ }
57
+ }
58
+
115
59
  export const setObjectProps = (diff/*: CommitDiff*/, object/*: ?Object3D*/)/*: void*/ => {
116
60
  setRef(object, diff);
117
61
  if (diff.next.pruned || !object)
118
62
  return;
119
63
  if (object instanceof Group)
120
64
  return setGroupProps(diff, object);
65
+ if (object instanceof DirectionalLight)
66
+ return setDirectionalLightProps(diff, object);
67
+ if (object instanceof OrthographicCamera) {
68
+ const diffs = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
69
+ for (const [key, diff] of diffs)
70
+ setObjectProp(diff, object);
71
+ object.updateProjectionMatrix();
72
+ }
121
73
  else {
122
74
  const diffs = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
123
75
  for (const [key, diff] of diffs)
package/reconciler.js CHANGED
@@ -3,20 +3,25 @@
3
3
  /*:: import type { Renderer } from '@lukekaalim/act-renderer-core'; */
4
4
 
5
5
  import { createTree } from "@lukekaalim/act-reconciler";
6
- import { createWebRenderer } from "@lukekaalim/act-web";
7
- import { attachNodes } from "@lukekaalim/act-web/node.js";
8
- import { createThreeRenderer } from "./renderer.js";
6
+ import { createWebRenderer, setNodeChildren } from "@lukekaalim/act-web";
7
+
8
+ import { createSceneRenderer } from "./renderer";
9
9
 
10
10
  export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
11
- const three = createThreeRenderer();
11
+
12
+ const scene = createSceneRenderer();
12
13
  const web = createWebRenderer({
13
- 'three': three,
14
+ 'scene': scene,
14
15
  });
15
- const onDiff = diff => attachNodes(node, web.render(diff));
16
+ const onDiff = diff => {
17
+ const children = web.render(diff);
18
+ setNodeChildren(diff, node, children)
19
+ };
16
20
 
17
21
  const options = {
18
22
  onDiff,
19
23
  scheduleWork: (c) => requestAnimationFrame(() => void c()),
24
+ cancelWork: (t) => cancelAnimationFrame(t),
20
25
  };
21
26
  const tree = createTree(element, options);
22
27
  };
package/renderer.js CHANGED
@@ -2,63 +2,10 @@
2
2
  /*:: import type { Renderer } from '@lukekaalim/act-renderer-core'; */
3
3
  /*:: import type { CommitDiff, PropDiff, CommitID } from '@lukekaalim/act-reconciler'; */
4
4
  /*:: import type { Object3D } from 'three'; */
5
- import { WebGLRenderer } from 'three';
6
- import { createManagedRenderer } from '@lukekaalim/act-renderer-core';
7
- import { setRootProps } from "./props.js";
5
+ import { createManagedRenderer, createNullRenderer } from '@lukekaalim/act-renderer-core';
8
6
  import { createObject } from "./objects";
9
7
  import { setObjectProps } from "./props";
10
8
 
11
- /*::
12
- export type Root = {
13
- canvas: HTMLCanvasElement,
14
- renderer: WebGLRenderer,
15
- frame: Frame
16
- };
17
- export type Frame = {
18
- scene: ?Object3D,
19
- id: ?AnimationFrameID
20
- }
21
- export type ObjectDef = {
22
- update: Object3D => mixed,
23
- create: () => Object3D
24
- };
25
- export type ObjectDefMap = { [type: string]: ObjectDef };
26
- */
27
-
28
- // A "root" is a combination of a canvas and a renderer
29
- export const createRootRenderer = (objectRenderer/*: Renderer<Object3D>*/)/*: Renderer<Node>*/ => {
30
- const roots/*: Map<CommitID, Root>*/ = new Map();
31
- const createRoot = (diff) => {
32
- const canvas = document.createElement('canvas');
33
- const renderer = new WebGLRenderer({ canvas });
34
-
35
- const root = { canvas, renderer, frame: { id: null, scene: null } };
36
- roots.set(diff.next.id, root);
37
- return root;
38
- };
39
- const removeRoot = (diff, root) => {
40
- if (root.frame.id)
41
- cancelAnimationFrame(root.frame.id)
42
-
43
- root.renderer.dispose();
44
- root.canvas.parentNode?.removeChild(root.canvas);
45
- roots.delete(diff.next.id);
46
- };
47
- const render = (diff) => {
48
- const root = roots.get(diff.next.id) || createRoot(diff);
49
-
50
- if (diff.next.pruned)
51
- return (removeRoot(diff, root), []);
52
-
53
- const children = diff.diffs.map(objectRenderer.render).flat(1);
54
- setRootProps(diff, root, children);
55
-
56
- return [root.canvas];
57
- };
58
- return {
59
- render
60
- };
61
- };
62
9
  export const createObjectRenderer = ()/*: Renderer<Object3D>*/ => {
63
10
  return createManagedRenderer({
64
11
  remove(object) {
@@ -69,7 +16,7 @@ export const createObjectRenderer = ()/*: Renderer<Object3D>*/ => {
69
16
  return;
70
17
  setObjectProps(diff, object);
71
18
  if (children.length > 0)
72
- object.add(...children);
19
+ object.add(...children.map(c => c.node));
73
20
  },
74
21
  add(diff) {
75
22
  if (typeof diff.next.element.type === 'function')
@@ -79,8 +26,7 @@ export const createObjectRenderer = ()/*: Renderer<Object3D>*/ => {
79
26
  });
80
27
  };
81
28
 
82
- export const createThreeRenderer = ()/*: Renderer<Node>*/ => {
29
+ export const createSceneRenderer = /*:: <T>*/()/*: Renderer<T>*/ => {
83
30
  const object = createObjectRenderer();
84
- const root = createRootRenderer(object);
85
- return root;
86
- };
31
+ return createNullRenderer(object);
32
+ }