@lukekaalim/act-three 5.6.4 → 5.8.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/objects.js CHANGED
@@ -1,6 +1,5 @@
1
1
  // @flow strict
2
- /*:: import type { Object3D } from "three"; */
3
- /*:: import type { PropDiff } from '@lukekaalim/act-reconciler'; */
2
+ /*:: import type { PropDiff } from '@lukekaalim/act-renderer-core'; */
4
3
  import {
5
4
  Group,
6
5
  Mesh,
@@ -20,12 +19,14 @@ import {
20
19
  Bone,
21
20
  Sprite,
22
21
  LOD,
22
+ Object3D,
23
23
  } from "three";
24
24
 
25
25
  export const threeObjectClasses/*: { [string]: Class<Object3D> }*/ = Object.fromEntries(Object.entries({
26
26
  // grouping
27
27
  Scene,
28
28
  Group,
29
+ Object3D,
29
30
 
30
31
  // lights
31
32
  DirectionalLight,
@@ -55,12 +56,13 @@ export const threeObjectClasses/*: { [string]: Class<Object3D> }*/ = Object.from
55
56
  LOD,
56
57
  }).map(([key, value]) => [key.toLowerCase(), (value/*: any*/)]));
57
58
 
58
- export const createObject = (type/*: string*/)/*: null | Object3D*/ => {
59
+ class UnsupportedObjectTypeError extends Error {}
60
+
61
+ export const createObject = (type/*: string*/)/*: Object3D*/ => {
59
62
  const objectClass = threeObjectClasses[type.toLowerCase()];
60
63
 
61
64
  if (!objectClass) {
62
- console.warn(`Unknown object type "${type}"`)
63
- return null;
65
+ throw new UnsupportedObjectTypeError()
64
66
  }
65
67
 
66
68
  return new objectClass();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukekaalim/act-three",
3
- "version": "5.6.4",
3
+ "version": "5.8.0",
4
4
  "description": "Render threejs content using act",
5
5
  "main": "entry.js",
6
6
  "type": "module",
@@ -12,12 +12,12 @@
12
12
  "author": "Luke Kaalim",
13
13
  "license": "ISC",
14
14
  "peerDependencies": {
15
- "three": "^0.141.0"
15
+ "three": "^0.149.0"
16
16
  },
17
17
  "dependencies": {
18
- "@lukekaalim/act": "^2.2.0",
18
+ "@lukekaalim/act": "^2.5.0",
19
+ "@lukekaalim/act-reconciler": "^3.1.0",
19
20
  "@lukekaalim/act-renderer-core": "^3.2.2",
20
- "@lukekaalim/act-reconciler": "^3.0.1",
21
21
  "@lukekaalim/act-web": "^2.0.1"
22
22
  }
23
23
  }
package/props.js CHANGED
@@ -1,78 +1,135 @@
1
1
  // @flow strict
2
- /*:: import type { Object3D, WebGLRenderer } from "three"; */
3
- /*:: import type { Props, PropDiff, CommitDiff } from '@lukekaalim/act-reconciler'; */
2
+ /*:: import type { WebGLRenderer, LightShadow } from "three"; */
3
+ /*::
4
+ import type { Props } from '@lukekaalim/act';
5
+ import type { PropDiff, PropDiffRegistry } from '@lukekaalim/act-renderer-core';
6
+ import type { CommitDiff, Diff3, DiffSet } from '@lukekaalim/act-reconciler';
7
+ */
4
8
 
5
- import { calculatePropsDiff } from "@lukekaalim/act-reconciler";
9
+ import { calculatePropsDiff, setRef2 } from '@lukekaalim/act-renderer-core'
6
10
  import { setHTMLProp } from "@lukekaalim/act-web";
7
- import { DirectionalLight, Group, Light, OrthographicCamera } from "three";
11
+ import {
12
+ BufferGeometry,
13
+ Camera,
14
+ DirectionalLight,
15
+ Euler,
16
+ Group,
17
+ Light,
18
+ Material,
19
+ Matrix3,
20
+ Matrix4,
21
+ Object3D,
22
+ OrthographicCamera,
23
+ Quaternion,
24
+ Vector2,
25
+ Vector3,
26
+ } from "three";
8
27
  import { setRef } from "@lukekaalim/act-renderer-core";
9
28
 
10
- export const setObjectProp = ({ key, prev, next }/*: PropDiff*/, object/*: Object3D*/)/*: void*/ => {
11
- switch (key) {
12
- case 'position':
13
- return object.position.copy((next/*: any*/));
14
- case 'quaternion':
15
- return void object.quaternion.copy((next/*: any*/));
16
- case 'rotation':
17
- return object.rotation.copy((next/*: any*/));
18
- case 'scale':
19
- return object.scale.copy((next/*: any*/));
20
- case 'matrix':
21
- return void object.matrix.copy((next/*: any*/));
22
- default:
23
- (object/*: any*/)[key] = next; break;
29
+ export const setTransformProp = (target/*: mixed*/, diff/*: PropDiff*/)/*: boolean*/ => {
30
+ if (target instanceof Vector2 && diff.next instanceof Vector2) {
31
+ target.copy(diff.next);
32
+ return true;
33
+ }
34
+ if (target instanceof Vector3 && diff.next instanceof Vector3) {
35
+ target.copy(diff.next);
36
+ return true;
37
+ }
38
+ if (target instanceof Matrix3 && diff.next instanceof Matrix3) {
39
+ target.copy(diff.next);
40
+ return true;
41
+ }
42
+ if (target instanceof Matrix4 && diff.next instanceof Matrix4) {
43
+ target.copy(diff.next);
44
+ return true;
45
+ }
46
+ if (target instanceof Quaternion && diff.next instanceof Quaternion) {
47
+ target.copy(diff.next);
48
+ return true;
49
+ }
50
+ if (target instanceof Euler && diff.next instanceof Euler) {
51
+ target.copy(diff.next);
52
+ return true;
24
53
  }
25
- };
54
+ return false;
55
+ }
56
+ export const setValueProp = (object/*: { [string]: mixed }*/, name/*: string*/, diff/*: PropDiff*/)/*: boolean*/ => {
57
+ if (typeof object[name] !== typeof diff.next)
58
+ return false;
26
59
 
27
- export const setGroupProps = (diff/*: CommitDiff*/, group/*: Group*/) => {
28
- const diffs = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
29
- for (const [key, diff] of diffs) {
30
- switch (key) {
31
- case 'group':
32
- if (diff.prev && diff.prev !== diff.next)
33
- group.remove((diff.prev/*: any*/));
34
- if (diff.next)
35
- group.add((diff.next/*: any*/));
36
- break;
37
- default:
38
- setObjectProp(diff, group); break;
39
- }
60
+ switch (typeof object[name]) {
61
+ case 'string':
62
+ case 'number':
63
+ case 'boolean':
64
+ object[name] = diff.next;
65
+ return true;
66
+ default:
67
+ return false;
68
+ }
69
+ }
70
+ export const setMeshProp = (object/*: { [string]: mixed }*/, name/*: string*/, diff/*: PropDiff*/)/*: boolean*/ => {
71
+ if (object[name] instanceof BufferGeometry && diff.next instanceof BufferGeometry) {
72
+ object[name] = diff.next;
73
+ return true;
74
+ }
75
+ if (object[name] instanceof Material && diff.next instanceof Material) {
76
+ object[name] = diff.next;
77
+ return true;
40
78
  }
41
- };
79
+ return false;
80
+ }
42
81
 
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;
82
+ export const setLightShadowProps = (
83
+ diffs/*: PropDiffRegistry*/,
84
+ lightShadow/*: LightShadow*/,
85
+ ) => {
86
+ for (const [key, propDiff] of diffs.map) {
87
+ const target = (lightShadow/*: any*/)[key];
88
+ if (setTransformProp(target, propDiff))
89
+ continue;
90
+ if (setValueProp((lightShadow/*: any*/), key, propDiff))
91
+ continue;
92
+ if (target instanceof Object3D) {
93
+ if (propDiff.next instanceof Object3D) {
94
+ (lightShadow/*: any*/)[key] = propDiff.next;
95
+ continue;
52
96
  }
53
- default:
54
- setObjectProp(diff, light); break;
97
+ const propRegistry = calculatePropsDiff((propDiff.prev/*: any*/) || {}, (propDiff.next/*: any*/) || {});
98
+ setObjectProps2(propRegistry, target);
99
+ continue;
55
100
  }
101
+
102
+ console.warn(`Unhandled prop ${key}`, propDiff.next, target);
56
103
  }
57
104
  }
58
105
 
59
- export const setObjectProps = (diff/*: CommitDiff*/, object/*: ?Object3D*/)/*: void*/ => {
60
- setRef(object, diff);
61
- if (diff.next.pruned || !object)
62
- return;
63
- if (object instanceof Group)
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
- }
73
- else {
74
- const diffs = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
75
- for (const [key, diff] of diffs)
76
- setObjectProp(diff, object);
106
+ const skipProps = new Set(['ref', 'key', 'children'])
107
+
108
+ export const setObjectProps2 = (
109
+ diff/*: PropDiffRegistry*/,
110
+ object/*: Object3D*/,
111
+ ) => {
112
+ for (const [key, propDiff] of diff.map) {
113
+ if (skipProps.has(key))
114
+ continue;
115
+
116
+ const target = (object/*: any*/)[key];
117
+ if (key === 'shadow' && object instanceof Light) {
118
+ const propRegistry = calculatePropsDiff((propDiff.prev/*: any*/) || {}, (propDiff.next/*: any*/) || {});
119
+ setLightShadowProps(propRegistry, target)
120
+ continue;
121
+ }
122
+ if (setMeshProp((object/*: any*/), key, propDiff)) {
123
+ continue;
124
+ }
125
+ if (setTransformProp(target, propDiff))
126
+ continue;
127
+ if (setValueProp((object/*: any*/), key, propDiff))
128
+ continue;
129
+
130
+ console.warn(`Unhandled prop ${key}`, propDiff.next, object);
77
131
  }
78
- };
132
+
133
+ if (object instanceof OrthographicCamera)
134
+ object.updateProjectionMatrix();
135
+ }
package/reconciler.js CHANGED
@@ -2,31 +2,62 @@
2
2
  /*:: import type { Element } from '@lukekaalim/act'; */
3
3
  /*:: import type { Renderer } from '@lukekaalim/act-renderer-core'; */
4
4
 
5
- import { createTree } from "@lukekaalim/act-reconciler";
5
+ import { createBoundaryService, createEffectService, createReconciler, createSchedule2, createTree, createTreeService2 } from "@lukekaalim/act-reconciler";
6
+ import { createNullRenderer2 } from "@lukekaalim/act-renderer-core";
6
7
  import { createWebRenderer, setNodeChildren } from "@lukekaalim/act-web";
8
+ import { setNodeChildren2 } from "@lukekaalim/act-web/node";
7
9
 
8
- import { createSceneRenderer } from "./renderer";
10
+ import { createObjectRenderer } from "./renderer.js";
9
11
 
10
12
  export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
13
+ const webToThree = createNullRenderer2(() => object, ['three']);
14
+ const threeToWeb = createNullRenderer2(() => web, ['web']);
11
15
 
12
- const scene = createSceneRenderer();
13
- const web = createWebRenderer(diff => {
14
- switch (diff.next.element.type) {
16
+ const object = createObjectRenderer((type) => {
17
+ switch (type) {
18
+ case 'web':
19
+ return threeToWeb;
20
+ default:
21
+ return null;
22
+ }
23
+ });
24
+ const web = createWebRenderer(type => {
25
+ switch (type) {
15
26
  case 'scene':
16
- return scene.render(diff);
27
+ case 'three':
28
+ return webToThree;
17
29
  default:
18
- return web.render(diff);
30
+ return null;
19
31
  }
20
32
  });
21
- const onDiff = diff => {
22
- const children = web.render(diff);
23
- setNodeChildren(diff, node, children)
24
- };
25
-
26
- const options = {
27
- onDiff,
28
- scheduleWork: (c) => requestAnimationFrame(() => void c()),
29
- cancelWork: (t) => cancelAnimationFrame(t),
30
- };
31
- const tree = createTree(element, options);
33
+
34
+ const scheduler = createSchedule2((callback) => {
35
+ const id = requestAnimationFrame(() => callback(5));
36
+ return () => cancelAnimationFrame(id);
37
+ })
38
+ const effect = createEffectService(scheduler);
39
+ const boundary = createBoundaryService();
40
+
41
+ const reconciler = createReconciler(scheduler);
42
+
43
+ reconciler.diff.subscribeDiff(set => {
44
+ performance.mark('act:three:nodes:start')
45
+ const children = web.render(set, set.root);
46
+ setNodeChildren2(node, children);
47
+ performance.mark('act:three:nodes:end')
48
+ performance.measure('act:three:nodes', 'act:three:nodes:start', 'act:three:nodes:end')
49
+
50
+ performance.mark('act:three:effects:start')
51
+ reconciler.tree.live.registry = effect.runEffectRegistry(set.registry);
52
+ performance.mark('act:three:effects:end')
53
+ performance.measure('act:three:effects', 'act:three:effects:start', 'act:three:effects:end')
54
+
55
+
56
+ performance.mark('act:three:suspense:start')
57
+ const map = boundary.calcBoundaryMap(set);
58
+ boundary.getRootBoundaryValue(set.suspensions, set, map);
59
+ performance.mark('act:three:suspense:end')
60
+ performance.measure('act:three:suspense', 'act:three:suspense:start', 'act:three:suspense:end')
61
+ })
62
+ reconciler.tree.mount(element);
32
63
  };
package/renderer.js CHANGED
@@ -1,48 +1,61 @@
1
1
  // @flow strict
2
- /*:: import type { Renderer, RenderResult} from '@lukekaalim/act-renderer-core'; */
3
- /*:: import type { Component } from '@lukekaalim/act'; */
4
- /*:: import type { CommitDiff, PropDiff, CommitID } from '@lukekaalim/act-reconciler'; */
5
- /*:: import type { Object3D } from 'three'; */
6
- import { createManagedRenderer, createNullRenderer } from '@lukekaalim/act-renderer-core';
2
+ /*:: import type { Renderer, RenderResult, PropDiff } from '@lukekaalim/act-renderer-core'; */
3
+ /*:: import type { Component, ElementType } from '@lukekaalim/act'; */
4
+ /*:: import type { CommitDiff, CommitID } from '@lukekaalim/act-reconciler'; */
5
+ /*::
6
+ import type { Object3D } from 'three';
7
+ import type { Renderer2 } from "../core/renderer2";
8
+ */
9
+ import { calculatePropsDiff, createManagedRenderer, createNullRenderer, createNullRenderer2, setRef2 } from '@lukekaalim/act-renderer-core';
10
+ import { createRenderer2 } from '@lukekaalim/act-renderer-core/renderer2';
7
11
  import { createObject } from "./objects";
8
- import { setObjectProps } from "./props";
12
+ import { setObjectProps2 } from "./props";
9
13
 
10
14
  export const createObjectRenderer = (
11
- next/*: ?((diff: CommitDiff, parent: ?Object3D) => RenderResult<Object3D>[])*/ = null,
12
- create/*: string => null | Object3D*/ = createObject,
13
- setProps/*: (CommitDiff, Object3D) => void*/ = setObjectProps,
14
- )/*: Renderer<Object3D>*/ => {
15
- const objectRenderer = createManagedRenderer({
15
+ getNextRenderer/*: null | ((type: ElementType) => null | Renderer2<Object3D>)*/ = null
16
+ )/*: Renderer2<Object3D>*/ => {
17
+
18
+ const getRenderer = (set, commitId) => {
19
+ const commit = set.nexts.get(commitId);
20
+ return getNextRenderer && getNextRenderer(commit.element.type) || objectRenderer;
21
+ }
22
+
23
+ const getNodes = (set, commitId) => {
24
+ const renderer = getRenderer(set, commitId)
25
+ return renderer.getNodes(set, commitId);
26
+ }
27
+
28
+ const render = (set, commitId) => {
29
+ const renderer = getRenderer(set, commitId)
30
+ return renderer.render(set, commitId);
31
+ }
32
+
33
+ const objectRenderer = createRenderer2({
16
34
  remove(object) {
17
35
  object.removeFromParent();
18
36
  },
19
- update(object, diff, children) {
20
- if (typeof diff.next.element.type === 'function')
21
- return;
22
- setProps(diff, object);
37
+ update(object, set, diff) {
38
+ setRef2(object, diff.commit, diff.change);
39
+ const prev = set.prevs.map.get(diff.commit.id);
40
+ const propDiff = calculatePropsDiff(prev ? prev.element.props : {}, diff.commit.element.props)
41
+ setObjectProps2(propDiff, object);
42
+ },
43
+ create(type) {
44
+ return createObject(type);
45
+ },
46
+ attach(object, set, diff, children) {
23
47
  const missingChildren = children
24
- .map(c => c.node)
25
48
  .filter(n => n.parent !== object);
49
+
26
50
  if (missingChildren.length > 0)
27
51
  object.add(...missingChildren);
28
- },
29
- add(diff) {
30
- if (typeof diff.next.element.type === 'function')
31
- return null;
32
- if (diff.next.element.type === 'act:context')
33
- return null;
34
- return create(diff.next.element.type);
35
- },
36
- next(diff, parent) {
37
- if (next)
38
- return next(diff, parent)
39
- return objectRenderer.render(diff);
40
52
  }
41
- });
53
+ }, { getNodes, render });
54
+
42
55
  return objectRenderer;
43
56
  };
44
57
 
45
- export const createSceneRenderer = /*:: <T>*/()/*: Renderer<T>*/ => {
58
+ export const createSceneRenderer = /*:: <T>*/()/*: Renderer2<T>*/ => {
46
59
  const object = createObjectRenderer();
47
- return createNullRenderer(object);
60
+ return createNullRenderer2(object);
48
61
  }