@lukekaalim/act-three 5.15.0 → 6.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/deps.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * as act from '@lukekaalim/act/mod.ts';
2
+ export * as recon from '@lukekaalim/act-recon/mod.ts';
3
+ export * as three from "three";
package/elements.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { DirectionalLight, Group, Line, LineLoop, LineSegments, Mesh, Object3D, OrthographicCamera, PerspectiveCamera, PointLight, Points, Scene, SkinnedMesh, Sprite } from "three";
2
+ import { act } from "./deps";
3
+ import { Object3DProps, setProps } from "./props";
4
+ import { Component, ElementType, h, Node } from "@lukekaalim/act";
5
+
6
+ const handlers = new Map<string | Symbol, ThreeNodeHandler<any>>();
7
+
8
+ export type ThreeNodeHandler<T extends Object3D> = {
9
+ type: string | Symbol,
10
+ create: (props: act.Props) => T,
11
+ setProps: (instance: T, prevProps: null | act.Props, nextProps: act.Props) => unknown,
12
+ }
13
+
14
+ export const registerElementHandler = <T extends Object3D>(handler: ThreeNodeHandler<T>): void => {
15
+ handlers.set(handler.type, handler);
16
+ }
17
+
18
+ export const getElementHandler = (type: act.ElementType): null | ThreeNodeHandler<Object3D> => {
19
+ if (typeof type === 'string' || typeof type === 'symbol') {
20
+ const handler = handlers.get(type);
21
+ if (handler)
22
+ return handler;
23
+ }
24
+ return null;
25
+ }
26
+
27
+ export type Newable<T> = { new (): T; };
28
+
29
+ export const addSimpleElement = <T extends Object3D>(type: string | symbol, ElementClass: Newable<T>) => {
30
+ registerElementHandler<T>({
31
+ type,
32
+ create() {
33
+ return new ElementClass();
34
+ },
35
+ setProps(instance, prevProps, nextProps) {
36
+ setProps(instance, nextProps as Object3DProps<T>);
37
+ },
38
+ })
39
+ }
40
+
41
+ const elementMap = {
42
+ mesh: Mesh,
43
+ points: Points,
44
+ group: Group,
45
+ scene: Scene,
46
+ sprite: Sprite,
47
+ skinnedMesh: SkinnedMesh,
48
+
49
+ pointLight: PointLight,
50
+ directionalLight: DirectionalLight,
51
+
52
+ line: Line,
53
+ lineLoop: LineLoop,
54
+ lineSegments: LineSegments,
55
+
56
+ perspectiveCamera: PerspectiveCamera,
57
+ orthographicCamera: OrthographicCamera,
58
+ } as const;
59
+ type ElementMap = typeof elementMap;
60
+
61
+ for (const key in elementMap) {
62
+ addSimpleElement(key, elementMap[key as keyof ElementMap] as any)
63
+ }
64
+
65
+ type ElementNodes = {
66
+ readonly [key in keyof ElementMap]: Component<Object3DProps<InstanceType<ElementMap[key]>>>
67
+ };
68
+
69
+ export const node: ElementNodes = Object.fromEntries(Object.keys(elementMap).map(k => [k, k as ElementType])) as ElementNodes;
package/mod.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { three } from './deps.ts';
2
+ export * from './space.ts';
3
+ export * from './render.ts';
4
+ export * from './props.ts';
5
+ export * from './elements.ts';
package/package.json CHANGED
@@ -1,27 +1,15 @@
1
1
  {
2
2
  "name": "@lukekaalim/act-three",
3
- "version": "5.15.0",
4
- "description": "Render threejs content using act",
5
- "main": "entry.js",
6
- "types": "entry.d.ts",
7
- "type": "module",
8
- "scripts": {
9
- "test": "cd ../../; npm test;",
10
- "postversion": "../../scripts/postversion.sh",
11
- "preversion": "npm run test"
12
- },
13
- "author": "Luke Kaalim",
14
- "license": "ISC",
15
- "peerDependencies": {
16
- "three": "^0.155.0"
17
- },
3
+ "main": "mod.ts",
4
+ "version": "6.0.0",
18
5
  "dependencies": {
19
- "@lukekaalim/act": "^2.5.0",
20
- "@lukekaalim/act-reconciler": "^3.7.5",
21
- "@lukekaalim/act-renderer-core": "^3.5.5",
22
- "@lukekaalim/act-web": "^2.5.5"
6
+ "@types/three": ">= 0.171.0",
7
+ "@lukekaalim/act-web": "^3.0.0",
8
+ "@lukekaalim/act": "^3.0.0",
9
+ "@lukekaalim/act-recon": "^1.0.0",
10
+ "@lukekaalim/act-backstage": "^1.0.0"
23
11
  },
24
- "devDependencies": {
25
- "@types/three": "^0.155.0"
12
+ "peerDependencies": {
13
+ "three": ">= 0.171.0"
26
14
  }
27
15
  }
package/props.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { Object3D } from "three";
2
+ import { act, three } from "./deps";
3
+
4
+ export type Object3DProps<T extends Object3D> = {
5
+ position?: three.Vector3,
6
+ scale?: three.Vector3,
7
+ quaternion?: three.Quaternion,
8
+ rotation?: three.Euler,
9
+
10
+ ref?: act.ReadonlyRef<null | three.Object3D>,
11
+ }
12
+ & (T extends three.Mesh ? { geometry?: three.BufferGeometry, material?: three.Material } : {})
13
+ & (T extends three.Scene ? { background?: three.Color } : {})
14
+
15
+ export const setProps = <T extends Object3D>(object: T, props: Object3DProps<T>) => {
16
+ if (props.position)
17
+ object.position.copy(props.position as three.Vector3);
18
+ if (props.scale)
19
+ object.scale.copy(props.scale as three.Vector3);
20
+ if (props.quaternion)
21
+ object.quaternion.copy(props.quaternion as three.Quaternion);
22
+ if (props.rotation)
23
+ object.rotation.copy(props.rotation as three.Euler);
24
+ if (props.ref)
25
+ (props.ref as Record<string, unknown>).current = object;
26
+
27
+ if (object instanceof three.Mesh) {
28
+ const meshProps = props as Object3DProps<three.Mesh>;
29
+ object.geometry = meshProps.geometry;
30
+ object.material = meshProps.material;
31
+ }
32
+ if (object instanceof three.PerspectiveCamera) {
33
+ object.updateProjectionMatrix();
34
+ }
35
+ if (object instanceof three.Scene) {
36
+ const sceneProps = props as Object3DProps<three.Scene>;
37
+ object.background = (sceneProps.background as three.Color);
38
+ }
39
+ };
package/render.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { createRenderFunction, RenderSpace, Scheduler } from "@lukekaalim/act-backstage";
2
+ import { createWebSpace } from '@lukekaalim/act-web';
3
+ import { createFinaleSpace } from "./space";
4
+
5
+ const intervalScheduler: Scheduler<NodeJS.Timeout> = {
6
+ duration: 10,
7
+ request: func => setInterval(func, 10),
8
+ cancel: id => clearInterval(id),
9
+ }
10
+
11
+ export const render = createRenderFunction<NodeJS.Timeout, HTMLElement>(intervalScheduler, (tree, root) => RenderSpace.combine([
12
+ createWebSpace(tree, root),
13
+ createFinaleSpace(tree),
14
+ ]));
package/space.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { createSimpleRenderSpace } from "@lukekaalim/act-backstage/mod.ts"
2
+ import { act, recon, three } from "./deps"
3
+ import { setProps } from "./props";
4
+ import { primitiveNodeTypes } from "@lukekaalim/act";
5
+ import { getElementHandler } from "./elements";
6
+
7
+ export const ThreeJS: act.Component = ({ children }) => act.h(primitiveNodeTypes.render, { type: 'threejs' }, children)
8
+
9
+ export const createFinaleSpace = (tree: recon.CommitTree) => {
10
+ return createSimpleRenderSpace(tree, {
11
+ rootTypes: new Set(['threejs']),
12
+ create(element) {
13
+ const handler = getElementHandler(element.type);
14
+ if (handler)
15
+ return handler.create(element.props);
16
+ return null;
17
+ },
18
+ update(el, next, prev) {
19
+ const handler = getElementHandler(next.type);
20
+ if (handler)
21
+ handler.setProps(el, prev && prev.props, next.props);
22
+ },
23
+ link(el, parent) {
24
+ parent && parent.add(el)
25
+ },
26
+ destroy(el) {
27
+ if (el.parent)
28
+ el.removeFromParent()
29
+ },
30
+ })
31
+ }
32
+
33
+ export const finale = createFinaleSpace;
@@ -1,37 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- // @flow strict
@@ -1,21 +0,0 @@
1
- // @flow strict
2
- /*::
3
- import type { SceneProps } from "../components";
4
- import type { Scene, Texture } from "three";
5
- */
6
- import { Color } from "three";
7
-
8
- /*::
9
- export type PropSetters<T> = {
10
- [string]: (object: T, nextProp: any, prevProp: any) => void,
11
- }
12
- */
13
-
14
- export const scenePropSetters/*: PropSetters<Scene>*/ = {
15
- 'background': (scene/*: Scene*/, prop/*: null | Color | Texture*/) => {
16
- if (scene.background instanceof Color && prop instanceof Color)
17
- scene.background.copy(prop)
18
- else
19
- scene.background = prop;
20
- }
21
- }
package/components.d.ts DELETED
@@ -1,129 +0,0 @@
1
- import { Component, Ref } from "@lukekaalim/act";
2
- import {
3
- AmbientLight,
4
- DirectionalLight,
5
- Euler,
6
- Group,
7
- HemisphereLight,
8
- Light,
9
- Line,
10
- LineLoop,
11
- LineSegments,
12
- Mesh,
13
- PerspectiveCamera,
14
- PointLight,
15
- Points,
16
- Quaternion,
17
- Scene,
18
- Sprite,
19
- Texture,
20
- Vector3,
21
- } from "three";
22
-
23
- export type ReferenceProps<T> = {
24
- ref?: ((reference: T) => unknown) | Ref<null | T>;
25
- };
26
-
27
- type PropsFromList<TObject, TPropList extends (keyof TObject)[]> = {
28
- [Property in TPropList[number]]?: undefined | TObject[Property];
29
- };
30
-
31
- export type Object3DProps<T> =
32
- & ReferenceProps<T>
33
- & {
34
- name?: string;
35
- position?: Vector3;
36
- rotation?: Euler;
37
- quaternion?: Quaternion;
38
- visible?: boolean;
39
- scale?: Vector3;
40
- };
41
-
42
- export const scene: Component<SceneProps>;
43
- export type SceneProps =
44
- & Object3DProps<Scene>
45
- & PropsFromList<Scene, [
46
- "fog",
47
- "backgroundBlurriness",
48
- "backgroundIntensity",
49
- "overrideMaterial",
50
- "background",
51
- "environment",
52
- ]>;
53
-
54
- export const group: Component<GroupProps>;
55
- export type GroupProps = Object3DProps<Group>;
56
-
57
- export const mesh: Component<MeshProps>;
58
- export type MeshProps =
59
- & Object3DProps<Mesh>
60
- & PropsFromList<Mesh, ["geometry", "material"]>;
61
-
62
- export const sprite: Component<SpriteProps>;
63
- export type SpriteProps =
64
- & Object3DProps<Sprite>
65
- & PropsFromList<Sprite, ["geometry", "material", "center"]>;
66
-
67
- export const points: Component<PointsProps>;
68
- export type PointsProps =
69
- & Object3DProps<Points>
70
- & PropsFromList<Points, ["geometry", "material"]>;
71
-
72
- export const line: Component<LineProps>;
73
- export const lineLoop: Component<LineProps>;
74
- export const lineSegments: Component<LineProps>;
75
- export type LineProps =
76
- & Object3DProps<Line>
77
- & PropsFromList<Line, ["geometry", "material"]>;
78
-
79
- export type LightProps = PropsFromList<Light, [
80
- "color",
81
- "intensity",
82
- "shadow",
83
- ]>;
84
-
85
- export const pointLight: Component<PointerLightProps>;
86
- export type PointerLightProps =
87
- & Object3DProps<PointLight>
88
- & LightProps
89
- & PropsFromList<PointLight, [
90
- "distance",
91
- "castShadow",
92
- "decay",
93
- "shadow",
94
- "power",
95
- ]>;
96
-
97
- export const ambientLight: Component<AmbientLightProps>;
98
- export type AmbientLightProps =
99
- & Object3DProps<AmbientLight>
100
- & LightProps;
101
-
102
- export const directionalLight: Component<DirectionalLightProps>;
103
- export type DirectionalLightProps =
104
- & Object3DProps<DirectionalLight>
105
- & LightProps
106
- & PropsFromList<DirectionalLight, [
107
- "shadow",
108
- "target",
109
- ]>;
110
-
111
- export const hemisphereLight: Component<HemisphereLightProps>;
112
- export type HemisphereLightProps =
113
- & Object3DProps<HemisphereLight>
114
- & LightProps;
115
-
116
- export const perspectiveCamera: Component<PerspectiveCameraProps>;
117
- export type PerspectiveCameraProps =
118
- & Object3DProps<PerspectiveCamera>
119
- & PropsFromList<PerspectiveCamera, [
120
- "aspect",
121
- "near",
122
- "far",
123
- "zoom",
124
- "fov",
125
- "focus",
126
- "view",
127
- "filmGauge",
128
- "filmOffset",
129
- ]>;
package/components.js DELETED
@@ -1,138 +0,0 @@
1
- // @flow strict
2
- /*:: import type { Component } from '@lukekaalim/act'; */
3
- /*:: import type {
4
- Vector3,
5
- Vector2,
6
- Object3D,
7
- Quaternion,
8
- BufferGeometry,
9
- Material,
10
- Texture,
11
- Color,
12
- Group,
13
- Camera,
14
- PerspectiveCamera,
15
- Euler,
16
- Scene,
17
- CubeTexture,
18
- Fog,
19
- Line,
20
- LineSegments,
21
- WebGLRenderer,
22
- } from "three"; */
23
- /*:: import * as Three from 'three'; */
24
-
25
- /*::
26
- export type RefProp<T> = ((reference: T) => mixed) | { current: T | any }
27
-
28
- export type Object3DProps<T> = {
29
- ref?: RefProp<?T>,
30
-
31
- name?: string,
32
- position?: Vector3,
33
- rotation?: Euler,
34
- quaternion?: Quaternion,
35
- visible?: boolean,
36
- scale?: Vector3,
37
- };
38
-
39
- export type SceneProps = {
40
- ...Object3DProps<Scene>,
41
- autoUpdate?: ?boolean,
42
- background?: ?(Color | Texture | CubeTexture),
43
- environment?: ?Texture,
44
- fog?: ?Fog,
45
- overrideMaterial?: ?Material
46
- };
47
-
48
- export type MeshProps = {
49
- ...Object3DProps<Three.Mesh>,
50
- geometry?: BufferGeometry,
51
- material?: Material,
52
- };
53
- export type SpriteProps = {
54
- ...Object3DProps<Three.Sprite>,
55
- material: Material;
56
- center: Vector2;
57
- };
58
- export type InstancedMeshProps = {
59
- ...MeshProps,
60
- ...Object3DProps<Three.InstancedMesh>,
61
- };
62
- export type PointLightsProps = {
63
- ...Object3DProps<Three.PointLight>,
64
- distance?: number,
65
- decay?: number,
66
- intensity?: number,
67
- power?: number,
68
- };
69
- export type DirectionalLightProps = {
70
- ...Object3DProps<Three.DirectionalLight>,
71
- distance?: number,
72
- decay?: number,
73
- intensity?: number,
74
- power?: number,
75
- };
76
- export type AmbientLightProps = {
77
- ...Object3DProps<Three.AmbientLight>,
78
- intensity?: number,
79
- power?: number,
80
- };
81
- export type PointsProps = {
82
- ...Object3DProps<Three.Points>,
83
- geometry?: BufferGeometry,
84
- material?: Material,
85
- };
86
- export type GroupProps = {
87
- ...Object3DProps<Three.Group>,
88
- group?: ?Three.Object3D,
89
- }
90
- export type PerspectiveCameraProps = {
91
- ...Object3DProps<Three.PerspectiveCamera>,
92
- aspect?: number,
93
- near?: number,
94
- far?: number,
95
- fov?: number,
96
- zoom?: number,
97
- }
98
- export type OrthographicCameraProps = {
99
- ...Object3DProps<Three.OrthographicCamera>,
100
- aspect?: number,
101
- near?: number,
102
- far?: number,
103
- zoom?: number,
104
- }
105
- */
106
-
107
- export const scene = ('scene'/*: Component<SceneProps>*/);
108
- export const group = ('group'/*: Component<GroupProps>*/);
109
-
110
- export const perspectiveCamera = ('perspectiveCamera'/*: Component<PerspectiveCameraProps>*/);
111
- export const orthographicCamera = ('orthographicCamera'/*: Component<OrthographicCameraProps>*/);
112
-
113
- export const mesh = ('mesh'/*: Component<MeshProps>*/);
114
- export const sprite = ('sprite'/*: Component<SpriteProps>*/);
115
- export const instancedMesh = ('instancedMesh'/*: Component<InstancedMeshProps>*/);
116
-
117
-
118
- export const points = ('points'/*: Component<PointsProps>*/);
119
-
120
- /*::
121
- export type LineProps = {
122
- ...Object3DProps<Line>,
123
- geometry?: BufferGeometry,
124
- material?: Material,
125
- }
126
- */
127
- export const line = ('line'/*: Component<LineProps>*/);
128
- export const lineLoop = ('lineLoop'/*: Component<LineProps>*/);
129
- export const lineSegments = ('lineSegments'/*: Component<LineProps>*/);
130
-
131
- export const pointLight = ('pointLight'/*: Component<PointLightsProps>*/);
132
- export const ambientLight = ('ambientLight'/*: Component<AmbientLightProps>*/);
133
- export const hemisphereLight = ('hemisphereLight'/*: Component<PointLightsProps>*/);
134
- export const directionalLight = ('directionalLight'/*: Component<DirectionalLightProps>*/);
135
-
136
-
137
- export * from './components/groups.js';
138
- export * from './components/lights.js';
package/entry.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './renderer.js';
2
- export * from './props.js';
3
- export * from './components.js';
4
- export * from './hooks.js';
5
- export * from './reconciler.js';
package/entry.js DELETED
@@ -1,8 +0,0 @@
1
- // @flow strict
2
-
3
- export * from './reconciler.js';
4
- export * from './renderer.js';
5
- export * from './objects.js';
6
- export * from './components.js';
7
- export * from './hooks.js';
8
- export * from './props.js';
package/hooks/matrix.js DELETED
@@ -1,28 +0,0 @@
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.d.ts DELETED
@@ -1,19 +0,0 @@
1
- import { Deps, Ref } from "@lukekaalim/act";
2
- import { WebGLRenderer } from "three";
3
-
4
- export type WebGLOptions = {
5
- antialias?: boolean,
6
- alpha?: boolean,
7
-
8
- shadowMap?: {
9
- enabled?: boolean,
10
- autoUpdate?: boolean,
11
- type?: number,
12
- }
13
- };
14
-
15
- export function useWebGLRenderer(
16
- canvasRef: Ref<null | HTMLCanvasElement>,
17
- options?: WebGLOptions,
18
- deps?: Deps
19
- ): null | WebGLRenderer
package/hooks.js DELETED
@@ -1,190 +0,0 @@
1
- // @flow strict
2
- /*:: import type { Ref } from '@lukekaalim/act'; */
3
- /*:: import type {
4
- Camera,
5
- Scene,
6
- Texture,
7
- Object3D,
8
- PerspectiveCamera,
9
- OrthographicCamera,
10
- } from "three"; */
11
- import { TextureLoader, Vector2, WebGLRenderer } from "three";
12
-
13
- import { useEffect, useState } from "@lukekaalim/act";
14
- import { useRef } from "@lukekaalim/act";
15
-
16
-
17
- /*::
18
- export type WebGLOptions = {
19
- antialias?: boolean,
20
- alpha?: boolean,
21
-
22
- shadowMap?: {
23
- enabled?: boolean,
24
- autoUpdate?: boolean,
25
- type?: number,
26
- }
27
- };
28
- */
29
-
30
- export const useWebGLRenderer = (
31
- canvasRef/*: Ref<?HTMLCanvasElement>*/,
32
- options/*: WebGLOptions*/ = {},
33
- deps/*: mixed[]*/ = []
34
- )/*: ?WebGLRenderer*/ => {
35
- const [renderer, setRenderer] = useState/*:: <?WebGLRenderer>*/(null);
36
- useEffect(() => {
37
- const { current: canvas } = canvasRef;
38
- if (!canvas)
39
- return;
40
-
41
- const renderer = new WebGLRenderer({ ...options, canvas });
42
- renderer.shadowMap.enabled = options.shadowMap?.enabled || false;
43
- setRenderer(renderer);
44
-
45
- return () => {
46
- setRenderer(null);
47
- renderer.dispose();
48
- }
49
- }, deps);
50
-
51
- return renderer;
52
- };
53
-
54
- // Resize the canvas's resolution whenever it changes size
55
- // useful for when the canvas is some percentage of the screen
56
- // like: width: 100%
57
- export const useResizingRenderer = (
58
- canvasRef/*: Ref<?HTMLCanvasElement>*/,
59
- renderer/*: ?WebGLRenderer*/,
60
- cameraRef/*: ?Ref<?PerspectiveCamera>*/ = null,
61
- onResize/*: () => mixed*/ = () => {},
62
- )/*: ?Vector2*/ => {
63
- const [size, setSize] = useState(null)
64
- useEffect(() => {
65
- const { current: canvas } = canvasRef;
66
- const { current: camera } = (cameraRef || { current: null })
67
- if (!canvas || !renderer)
68
- return null;
69
-
70
- const observer = new ResizeObserver((entries) => {
71
- for (const entry of entries.filter(e => e.target === canvas)) {
72
- const width = entry.contentBoxSize[0].inlineSize;
73
- const height = entry.contentBoxSize[0].blockSize;
74
- const size = new Vector2(width, height);
75
- setSize(size);
76
- renderer.setSize(width, height, false);
77
- if (camera) {
78
- camera.aspect = width / height;
79
- camera.updateProjectionMatrix();
80
- }
81
- onResize();
82
- }
83
- });
84
- observer.observe(canvas, { box: 'content-box' });
85
-
86
- return () => {
87
- observer.unobserve(canvas);
88
- setSize(null);
89
- }
90
- }, [renderer]);
91
-
92
- return size;
93
- }
94
-
95
- export const useAnimationFrame = (
96
- onAnimate/*: ?(now: DOMHighResTimeStamp, delta: number) => mixed*/ = null,
97
- deps/*: mixed[]*/ = []
98
- ) => {
99
- useEffect(() => {
100
- if (!onAnimate)
101
- return;
102
-
103
- let lastFrame = performance.now();
104
- const animate = (now) => {
105
- const delta = now - lastFrame;
106
- lastFrame = now;
107
-
108
- onAnimate(now, delta);
109
- id = requestAnimationFrame(animate);
110
- };
111
- let id = requestAnimationFrame(animate);
112
-
113
- return () => {
114
- if (id)
115
- cancelAnimationFrame(id);
116
- }
117
- }, deps);
118
- }
119
-
120
- export const useRenderLoop = /*:: <TCamera: Camera, TScene: Object3D>*/(
121
- renderer/*: ?WebGLRenderer*/,
122
- camera/*: Ref<?TCamera>*/,
123
- scene/*: Ref<?TScene>*/,
124
- onAnimate/*: (now: DOMHighResTimeStamp, delta: number) => mixed*/ = (_, __) => {},
125
- deps/*: mixed[]*/ = []
126
- ) => {
127
- const renderFunction = renderer && ((now, delta) => {
128
- onAnimate(now, delta);
129
- if (camera.current && scene.current)
130
- renderer.render(scene.current, camera.current);
131
- });
132
-
133
- useEffect(() => {
134
- if (renderFunction)
135
- renderFunction(performance.now(), 0);
136
- }, [renderer, ...deps])
137
-
138
- useAnimationFrame(renderFunction, [renderer, ...deps])
139
- }
140
-
141
- /*::
142
- export interface Disposable {
143
- dispose(): void;
144
- }
145
- */
146
-
147
- export const useDisposable = /*:: <T: Disposable>*/(
148
- create/*: () => T*/,
149
- deps/*: mixed[]*/ = []
150
- )/*: T*/ => {
151
- const [resource, setResource] = useState/*:: <T>*/(() => create());
152
- const disposedRef = useRef(false);
153
-
154
- useEffect(() => {
155
- const { current: disposed } = disposedRef
156
-
157
- if (disposed) {
158
- setResource(create());
159
- disposedRef.current = false;
160
- }
161
-
162
- return () => {
163
- resource.dispose();
164
- disposedRef.current = true;
165
- }
166
- }, deps);
167
- return resource;
168
- };
169
-
170
- export const useTexture = (url/*: string*/)/*: Texture*/ => {
171
- const [texture, setTexture] = useState(null)
172
- useEffect(() => {
173
- const loader = new TextureLoader();
174
-
175
- const texture = loader.load(url);
176
- setTexture(texture);
177
-
178
- return () => {
179
- texture.dispose();
180
- }
181
- }, []);
182
-
183
- if (!texture)
184
- throw { type: 'loading' };
185
-
186
- return texture;
187
- };
188
-
189
-
190
- export * from './hooks/matrix.js';
package/objects.js DELETED
@@ -1,69 +0,0 @@
1
- // @flow strict
2
- /*:: import type { PropDiff } from '@lukekaalim/act-renderer-core'; */
3
- import {
4
- Group,
5
- Mesh,
6
- PerspectiveCamera,
7
- PointLight,
8
- Points,
9
- Scene,
10
- InstancedMesh,
11
- OrthographicCamera,
12
- DirectionalLight,
13
- HemisphereLight,
14
- AmbientLight,
15
- Skeleton,
16
- LineSegments,
17
- Line,
18
- LineLoop,
19
- Bone,
20
- Sprite,
21
- LOD,
22
- Object3D,
23
- } from "three";
24
-
25
- export const threeObjectClasses/*: { [string]: Class<Object3D> }*/ = Object.fromEntries(Object.entries({
26
- // grouping
27
- Scene,
28
- Group,
29
- Object3D,
30
-
31
- // lights
32
- DirectionalLight,
33
- HemisphereLight,
34
- PointLight,
35
- AmbientLight,
36
-
37
- // camera
38
- PerspectiveCamera,
39
- OrthographicCamera,
40
-
41
- // lines
42
- Points,
43
- LineSegments,
44
- Line,
45
- LineLoop,
46
-
47
- // skinning
48
- Skeleton,
49
- Bone,
50
-
51
- Sprite,
52
-
53
- // mesh
54
- Mesh,
55
- InstancedMesh,
56
- LOD,
57
- }).map(([key, value]) => [key.toLowerCase(), (value/*: any*/)]));
58
-
59
- class UnsupportedObjectTypeError extends Error {}
60
-
61
- export const createObject = (type/*: string*/)/*: Object3D*/ => {
62
- const objectClass = threeObjectClasses[type.toLowerCase()];
63
-
64
- if (!objectClass) {
65
- throw new UnsupportedObjectTypeError()
66
- }
67
-
68
- return new objectClass();
69
- };
package/props.d.ts DELETED
@@ -1,7 +0,0 @@
1
- import { Object3D } from "three";
2
- import { PropDiffRegistry } from "@lukekaalim/act-renderer-core";
3
-
4
- declare function setObjectProps2(
5
- diff: PropDiffRegistry,
6
- object: Object3D
7
- ): void;
package/props.js DELETED
@@ -1,160 +0,0 @@
1
- // @flow strict
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
- */
8
-
9
- import { calculatePropsDiff, setRef2 } from '@lukekaalim/act-renderer-core'
10
- import { setHTMLProp } from "@lukekaalim/act-web";
11
- import {
12
- BufferGeometry,
13
- Camera,
14
- Color,
15
- DirectionalLight,
16
- Euler,
17
- Group,
18
- Light,
19
- Material,
20
- Matrix3,
21
- Matrix4,
22
- Object3D,
23
- OrthographicCamera,
24
- Quaternion,
25
- Scene,
26
- Vector2,
27
- Vector3,
28
- } from "three";
29
- import { setRef } from "@lukekaalim/act-renderer-core";
30
- import { scenePropSetters } from './components/scene';
31
-
32
- export const setTransformProp = (target/*: mixed*/, diff/*: PropDiff*/)/*: boolean*/ => {
33
- if (target instanceof Vector2 && diff.next instanceof Vector2) {
34
- target.copy(diff.next);
35
- return true;
36
- }
37
- if (target instanceof Vector3 && diff.next instanceof Vector3) {
38
- target.copy(diff.next);
39
- return true;
40
- }
41
- if (target instanceof Matrix3 && diff.next instanceof Matrix3) {
42
- target.copy(diff.next);
43
- return true;
44
- }
45
- if (target instanceof Matrix4 && diff.next instanceof Matrix4) {
46
- target.copy(diff.next);
47
- return true;
48
- }
49
- if (target instanceof Quaternion && diff.next instanceof Quaternion) {
50
- target.copy(diff.next);
51
- return true;
52
- }
53
- if (target instanceof Euler && diff.next instanceof Euler) {
54
- target.copy(diff.next);
55
- return true;
56
- }
57
- return false;
58
- }
59
- export const setValueProp = (object/*: { [string]: mixed }*/, name/*: string*/, diff/*: PropDiff*/)/*: boolean*/ => {
60
- if (typeof object[name] !== typeof diff.next)
61
- return false;
62
-
63
- switch (typeof object[name]) {
64
- case 'string':
65
- case 'number':
66
- case 'boolean':
67
- object[name] = diff.next;
68
- return true;
69
- default:
70
- return false;
71
- }
72
- }
73
- export const setMeshProp = (object/*: { [string]: mixed }*/, name/*: string*/, diff/*: PropDiff*/)/*: boolean*/ => {
74
- if (object[name] instanceof BufferGeometry && diff.next instanceof BufferGeometry) {
75
- object[name] = diff.next;
76
- return true;
77
- }
78
- if (object[name] instanceof Material && diff.next instanceof Material) {
79
- object[name] = diff.next;
80
- return true;
81
- }
82
- return false;
83
- }
84
- export const setInstanceProp = (object/*: { [string]: mixed }*/, name/*: string*/, diff/*: PropDiff*/)/*: boolean*/ => {
85
- if (object[name] instanceof Color) {
86
- if (diff.next instanceof Color || typeof diff.next === 'string' || typeof diff.next === 'number') {
87
- object[name].set(diff.next)
88
- return true;
89
- }
90
- }
91
- return false;
92
- }
93
-
94
- export const setLightShadowProps = (
95
- diffs/*: PropDiffRegistry*/,
96
- lightShadow/*: LightShadow*/,
97
- ) => {
98
- for (const [key, propDiff] of diffs.map) {
99
- const target = (lightShadow/*: any*/)[key];
100
- if (setTransformProp(target, propDiff))
101
- continue;
102
- if (setValueProp((lightShadow/*: any*/), key, propDiff))
103
- continue;
104
- if (target instanceof Object3D) {
105
- if (propDiff.next instanceof Object3D) {
106
- (lightShadow/*: any*/)[key] = propDiff.next;
107
- continue;
108
- }
109
- const propRegistry = calculatePropsDiff((propDiff.prev/*: any*/) || {}, (propDiff.next/*: any*/) || {});
110
- setObjectProps2(propRegistry, target);
111
- continue;
112
- }
113
-
114
- console.warn(`Unhandled prop ${key}`, propDiff.next, target);
115
- }
116
- }
117
-
118
- const skipProps = new Set(['ref', 'key', 'children'])
119
-
120
- export const setObjectProps2 = (
121
- diff/*: PropDiffRegistry*/,
122
- object/*: Object3D*/,
123
- ) => {
124
- for (const [key, propDiff] of diff.map) {
125
- if (skipProps.has(key))
126
- continue;
127
-
128
- const target = (object/*: any*/)[key];
129
- if (key === 'shadow' && object instanceof Light) {
130
- const propRegistry = calculatePropsDiff((propDiff.prev/*: any*/) || {}, (propDiff.next/*: any*/) || {});
131
- setLightShadowProps(propRegistry, target)
132
- continue;
133
- }
134
- if (setInstanceProp((object/*: any*/), key, propDiff))
135
- continue;
136
-
137
- if (setMeshProp((object/*: any*/), key, propDiff)) {
138
- continue;
139
- }
140
- if (setTransformProp(target, propDiff))
141
- continue;
142
- if (setValueProp((object/*: any*/), key, propDiff))
143
- continue;
144
- if (object instanceof Scene) {
145
- const setter = scenePropSetters[propDiff.key];
146
- if (setter) {
147
- setter(object, propDiff.next, propDiff.prev);
148
- continue;
149
- }
150
- }
151
-
152
-
153
-
154
- console.warn(`Unhandled prop ${key}`, propDiff.next, object);
155
- }
156
-
157
- if (object instanceof OrthographicCamera)
158
- object.updateProjectionMatrix();
159
-
160
- }
package/reconciler.d.ts DELETED
@@ -1,3 +0,0 @@
1
- import { Element } from "@lukekaalim/act";
2
-
3
- export function render(element: Element, node: HTMLElement): void;
package/reconciler.js DELETED
@@ -1,54 +0,0 @@
1
- // @flow strict
2
- /*:: import type { Element } from '@lukekaalim/act'; */
3
- /*:: import type { Renderer } from '@lukekaalim/act-renderer-core'; */
4
-
5
- import { createBoundaryService, createEffectService, createReconciler, createSchedule2, createTree, createTreeService2 } from "@lukekaalim/act-reconciler";
6
- import { createNullRenderer2 } from "@lukekaalim/act-renderer-core";
7
- import { createWebRenderer, setNodeChildren } from "@lukekaalim/act-web";
8
- import { setNodeChildren2 } from "@lukekaalim/act-web/node";
9
-
10
- import { createObjectRenderer } from "./renderer.js";
11
-
12
- export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
13
- const webToThree = createNullRenderer2(() => object, ['three']);
14
- const threeToWeb = createNullRenderer2(() => web, ['web']);
15
-
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) {
26
- case 'scene':
27
- case 'three':
28
- return webToThree;
29
- default:
30
- return null;
31
- }
32
- });
33
-
34
- const scheduler = createSchedule2((callback) => {
35
- const id = requestAnimationFrame(() => callback(16));
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
- const children = web.render(set, set.root);
45
- setNodeChildren2(node, children);
46
-
47
- reconciler.tree.live.registry = effect.runEffectRegistry(set.registry);
48
-
49
-
50
- const map = boundary.calcBoundaryMap(set);
51
- boundary.getRootBoundaryValue(set.suspensions, set, map);
52
- })
53
- reconciler.tree.mount(element);
54
- };
package/renderer.d.ts DELETED
@@ -1,13 +0,0 @@
1
- import { ElementType } from "@lukekaalim/act";
2
- import { Renderer2 } from "@lukekaalim/act-renderer-core";
3
- import { Object3D } from "three";
4
-
5
- export declare function createObjectRenderer(
6
- getNextRenderer?: null | ((type: ElementType) => null | Renderer2<Object3D>)
7
- ): Renderer2<Object3D>;
8
-
9
- /**
10
- * A scene renderer is just an object renderer connected to
11
- * a null renderer
12
- */
13
- export declare function createSceneRenderer<T>(): Renderer2<T>;
package/renderer.js DELETED
@@ -1,60 +0,0 @@
1
- // @flow strict
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, createRenderer2, createNullRenderer2, setRef2 } from '@lukekaalim/act-renderer-core';
10
- import { createObject } from "./objects";
11
- import { setObjectProps2 } from "./props";
12
-
13
- export const createObjectRenderer = (
14
- getNextRenderer/*: null | ((type: ElementType) => null | Renderer2<Object3D>)*/ = null
15
- )/*: Renderer2<Object3D>*/ => {
16
-
17
- const getRenderer = (set, commitId) => {
18
- const commit = set.nexts.map.get(commitId) || set.prevs.get(commitId);
19
- return getNextRenderer && getNextRenderer(commit.element.type) || objectRenderer;
20
- }
21
-
22
- const getNodes = (set, commitId) => {
23
- const renderer = getRenderer(set, commitId)
24
- return renderer.getNodes(set, commitId);
25
- }
26
-
27
- const render = (set, commitId) => {
28
- const renderer = getRenderer(set, commitId)
29
- return renderer.render(set, commitId);
30
- }
31
-
32
- const objectRenderer = createRenderer2({
33
- remove(object) {
34
- object.removeFromParent();
35
- },
36
- update(object, set, diff) {
37
- setRef2(object, diff.commit, diff.change);
38
- const prev = set.prevs.map.get(diff.commit.id);
39
- const propDiff = calculatePropsDiff(prev ? prev.element.props : {}, diff.commit.element.props)
40
- setObjectProps2(propDiff, object);
41
- },
42
- create(type) {
43
- return createObject(type);
44
- },
45
- attach(object, set, diff, children) {
46
- const missingChildren = children
47
- .filter(n => n.parent !== object);
48
-
49
- if (missingChildren.length > 0)
50
- object.add(...missingChildren);
51
- }
52
- }, { getNodes, render });
53
-
54
- return objectRenderer;
55
- };
56
-
57
- export const createSceneRenderer = /*:: <T>*/()/*: Renderer2<T>*/ => {
58
- const object = createObjectRenderer();
59
- return createNullRenderer2(object);
60
- }