@lukekaalim/act-three 4.0.0 → 5.0.0
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.
- package/components/groups.js +26 -0
- package/components/lights.js +1 -0
- package/components.js +31 -14
- package/entry.js +2 -1
- package/hooks.js +165 -0
- package/objects.js +34 -17
- package/package.json +3 -3
- package/props.js +25 -73
- package/reconciler.js +10 -6
- package/renderer.js +5 -59
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
/*:: import type { Component, Ref } from '@lukekaalim/act'; */
|
|
3
|
+
/*:: import type { Euler, Group } 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 LookAtGroup/*: Component<{ ...$Diff<GroupProps, { rotation?: Euler, ref?: RefProp<Group> }>, target: Vector3 }>*/ = ({
|
|
11
|
+
target,
|
|
12
|
+
children,
|
|
13
|
+
position = new Vector3(0, 0, 0),
|
|
14
|
+
...groupProps
|
|
15
|
+
}) => {
|
|
16
|
+
const [groupNode, setGroup] = useState(null);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!groupNode)
|
|
19
|
+
return;
|
|
20
|
+
|
|
21
|
+
groupNode.setRotationFromMatrix(new Matrix4().lookAt(position, target, new Vector3(0, 1, 0)));
|
|
22
|
+
groupNode.updateMatrix();
|
|
23
|
+
}, [groupNode, target.x, target.y, target.z, position.x, position.y, position.z]);
|
|
24
|
+
|
|
25
|
+
return h(group, { ...groupProps, position, ref: setGroup }, children);
|
|
26
|
+
}
|
|
@@ -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
|
|
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?:
|
|
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
|
-
|
|
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
package/hooks.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
canvas/*: ?HTMLCanvasElement*/,
|
|
25
|
+
options/*: WebGLOptions*/ = {},
|
|
26
|
+
deps/*: mixed[]*/ = []
|
|
27
|
+
)/*: ?WebGLRenderer*/ => {
|
|
28
|
+
const [renderer, setRenderer] = useState/*:: <?WebGLRenderer>*/(null);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!canvas)
|
|
31
|
+
return;
|
|
32
|
+
|
|
33
|
+
const renderer = new WebGLRenderer({ ...options, canvas });
|
|
34
|
+
renderer.shadowMap.enabled = options.shadowMap?.enabled || false;
|
|
35
|
+
setRenderer(renderer);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
setRenderer(null);
|
|
39
|
+
renderer.dispose();
|
|
40
|
+
}
|
|
41
|
+
}, [canvas, ...deps]);
|
|
42
|
+
|
|
43
|
+
return renderer;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Resize the canvas's resolution whenever it changes size
|
|
47
|
+
// useful for when the canvas is some percentage of the screen
|
|
48
|
+
// like: width: 100%
|
|
49
|
+
export const useResizingRenderer = (canvas/*: ?HTMLCanvasElement*/, renderer/*: ?WebGLRenderer*/)/*: ?Vector2*/ => {
|
|
50
|
+
const [size, setSize] = useState(null)
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!canvas || !renderer)
|
|
53
|
+
return null;
|
|
54
|
+
|
|
55
|
+
const observer = new ResizeObserver((entries) => {
|
|
56
|
+
for (const entry of entries.filter(e => e.target === canvas)) {
|
|
57
|
+
const width = entry.contentBoxSize[0].inlineSize;
|
|
58
|
+
const height = entry.contentBoxSize[0].blockSize;
|
|
59
|
+
const size = new Vector2(width, height);
|
|
60
|
+
setSize(size);
|
|
61
|
+
renderer.setSize(width, height, false);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
observer.observe(canvas, { box: 'content-box' });
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
observer.unobserve(canvas);
|
|
68
|
+
setSize(null);
|
|
69
|
+
}
|
|
70
|
+
}, [canvas, renderer]);
|
|
71
|
+
|
|
72
|
+
return size;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const useAnimationFrame = (
|
|
76
|
+
onAnimate/*: (now: DOMHighResTimeStamp, delta: number) => mixed*/,
|
|
77
|
+
deps/*: mixed[]*/ = []
|
|
78
|
+
) => {
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
let lastFrame = performance.now();
|
|
81
|
+
const animate = (now) => {
|
|
82
|
+
const delta = now - lastFrame;
|
|
83
|
+
lastFrame = now;
|
|
84
|
+
|
|
85
|
+
onAnimate(now, delta);
|
|
86
|
+
id = requestAnimationFrame(animate);
|
|
87
|
+
};
|
|
88
|
+
let id = requestAnimationFrame(animate);
|
|
89
|
+
|
|
90
|
+
return () => {
|
|
91
|
+
if (id)
|
|
92
|
+
cancelAnimationFrame(id);
|
|
93
|
+
}
|
|
94
|
+
}, deps);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const useRenderLoop = (
|
|
98
|
+
renderer/*: ?WebGLRenderer*/,
|
|
99
|
+
camera/*: ?Camera*/,
|
|
100
|
+
scene/*: ?Scene*/,
|
|
101
|
+
onAnimate/*: (now: DOMHighResTimeStamp, delta: number) => mixed*/ = (_, __) => {},
|
|
102
|
+
deps/*: mixed[]*/ = []
|
|
103
|
+
) => {
|
|
104
|
+
const renderFunction = renderer && camera && scene ? (now, delta) => {
|
|
105
|
+
onAnimate(now, delta);
|
|
106
|
+
renderer.render(scene, camera);
|
|
107
|
+
} : () => {};
|
|
108
|
+
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (scene && camera && renderer)
|
|
111
|
+
renderer.render(scene, camera);
|
|
112
|
+
}, [renderer, camera, scene, ...deps])
|
|
113
|
+
|
|
114
|
+
useAnimationFrame(renderFunction, [renderer, camera, scene, ...deps])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*::
|
|
118
|
+
export interface Disposable {
|
|
119
|
+
dispose(): void;
|
|
120
|
+
}
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
export const useDisposable = /*:: <T: Disposable>*/(
|
|
124
|
+
create/*: () => T*/,
|
|
125
|
+
deps/*: mixed[]*/ = []
|
|
126
|
+
)/*: T*/ => {
|
|
127
|
+
const [resource, setResource] = useState/*:: <T>*/(() => create());
|
|
128
|
+
const disposedRef = useRef(false);
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
const { current: disposed } = disposedRef
|
|
132
|
+
|
|
133
|
+
if (disposed) {
|
|
134
|
+
setResource(create());
|
|
135
|
+
disposedRef.current = false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return () => {
|
|
139
|
+
resource.dispose();
|
|
140
|
+
disposedRef.current = true;
|
|
141
|
+
}
|
|
142
|
+
}, deps);
|
|
143
|
+
return resource;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const useTexture = (url/*: string*/)/*: Texture*/ => {
|
|
147
|
+
const [texture, setTexture] = useState(null)
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
const loader = new TextureLoader();
|
|
150
|
+
|
|
151
|
+
const texture = loader.load(url);
|
|
152
|
+
setTexture(texture);
|
|
153
|
+
|
|
154
|
+
console.log(loader);
|
|
155
|
+
|
|
156
|
+
return () => {
|
|
157
|
+
texture.dispose();
|
|
158
|
+
}
|
|
159
|
+
}, []);
|
|
160
|
+
|
|
161
|
+
if (!texture)
|
|
162
|
+
throw { type: 'loading' };
|
|
163
|
+
|
|
164
|
+
return texture;
|
|
165
|
+
};
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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": "
|
|
3
|
+
"version": "5.0.0",
|
|
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": "^
|
|
20
|
-
"@lukekaalim/act-reconciler": "^2.0
|
|
19
|
+
"@lukekaalim/act-renderer-core": "^3.0.0",
|
|
20
|
+
"@lukekaalim/act-reconciler": "^2.1.0",
|
|
21
21
|
"@lukekaalim/act-web": "^1.0.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,16 +3,20 @@
|
|
|
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
|
-
|
|
8
|
-
import {
|
|
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
|
-
|
|
11
|
+
|
|
12
|
+
const scene = createSceneRenderer();
|
|
12
13
|
const web = createWebRenderer({
|
|
13
|
-
'
|
|
14
|
+
'scene': scene,
|
|
14
15
|
});
|
|
15
|
-
const onDiff = 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,
|
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 {
|
|
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
|
|
29
|
+
export const createSceneRenderer = /*:: <T>*/()/*: Renderer<T>*/ => {
|
|
83
30
|
const object = createObjectRenderer();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
};
|
|
31
|
+
return createNullRenderer(object);
|
|
32
|
+
}
|